blob: 0baf7c8e594b67f538c45f75a4d69d4107a90533 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000039#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000040#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020043#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010044
45#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010046#if ENABLE_SH_MATH_SUPPORT
47# include "math.h"
48#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020049#if ENABLE_ASH_RANDOM_SUPPORT
50# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020051#else
52# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020053#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000054
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020055#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010056#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000057/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020058# undef CONFIG_FEATURE_SH_STANDALONE
59# undef ENABLE_FEATURE_SH_STANDALONE
60# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010061# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020062# define ENABLE_FEATURE_SH_STANDALONE 0
63# define IF_FEATURE_SH_STANDALONE(...)
64# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000065#endif
66
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000067#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000068# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000069#endif
70
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010071#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000072# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000073#endif
74
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +010075//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
76//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
77//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
Denys Vlasenko771f1992010-07-16 14:31:34 +020078
Denys Vlasenkod383b492010-09-06 10:22:13 +020079//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko771f1992010-07-16 14:31:34 +020080//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
81
82//config:config ASH
83//config: bool "ash"
84//config: default y
85//config: depends on !NOMMU
86//config: help
87//config: Tha 'ash' shell adds about 60k in the default configuration and is
88//config: the most complete and most pedantically correct shell included with
89//config: busybox. This shell is actually a derivative of the Debian 'dash'
90//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
91//config: (written by Kenneth Almquist) from NetBSD.
92//config:
93//config:config ASH_BASH_COMPAT
94//config: bool "bash-compatible extensions"
95//config: default y
96//config: depends on ASH
97//config: help
98//config: Enable bash-compatible extensions.
99//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +0100100//config:config ASH_IDLE_TIMEOUT
101//config: bool "Idle timeout variable"
102//config: default n
103//config: depends on ASH
104//config: help
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100105//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko046341e2011-02-04 17:53:59 +0100106//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200107//config:config ASH_JOB_CONTROL
108//config: bool "Job control"
109//config: default y
110//config: depends on ASH
111//config: help
112//config: Enable job control in the ash shell.
113//config:
114//config:config ASH_ALIAS
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100115//config: bool "Alias support"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200116//config: default y
117//config: depends on ASH
118//config: help
119//config: Enable alias support in the ash shell.
120//config:
121//config:config ASH_GETOPTS
122//config: bool "Builtin getopt to parse positional parameters"
123//config: default y
124//config: depends on ASH
125//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100126//config: Enable support for getopts builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200127//config:
128//config:config ASH_BUILTIN_ECHO
129//config: bool "Builtin version of 'echo'"
130//config: default y
131//config: depends on ASH
132//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100133//config: Enable support for echo builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config:
135//config:config ASH_BUILTIN_PRINTF
136//config: bool "Builtin version of 'printf'"
137//config: default y
138//config: depends on ASH
139//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100140//config: Enable support for printf builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200141//config:
142//config:config ASH_BUILTIN_TEST
143//config: bool "Builtin version of 'test'"
144//config: default y
145//config: depends on ASH
146//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100147//config: Enable support for test builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200148//config:
149//config:config ASH_CMDCMD
150//config: bool "'command' command to override shell builtins"
151//config: default y
152//config: depends on ASH
153//config: help
154//config: Enable support for the ash 'command' builtin, which allows
155//config: you to run the specified command with the specified arguments,
156//config: even when there is an ash builtin command with the same name.
157//config:
158//config:config ASH_MAIL
159//config: bool "Check for new mail on interactive shells"
160//config: default n
161//config: depends on ASH
162//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100163//config: Enable "check for new mail" function in the ash shell.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200164//config:
165//config:config ASH_OPTIMIZE_FOR_SIZE
166//config: bool "Optimize for size instead of speed"
167//config: default y
168//config: depends on ASH
169//config: help
170//config: Compile ash for reduced size at the price of speed.
171//config:
172//config:config ASH_RANDOM_SUPPORT
173//config: bool "Pseudorandom generator and $RANDOM variable"
174//config: default y
175//config: depends on ASH
176//config: help
177//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
178//config: Each read of "$RANDOM" will generate a new pseudorandom value.
179//config: You can reset the generator by using a specified start value.
180//config: After "unset RANDOM" the generator will switch off and this
181//config: variable will no longer have special treatment.
182//config:
183//config:config ASH_EXPAND_PRMT
184//config: bool "Expand prompt string"
185//config: default y
186//config: depends on ASH
187//config: help
188//config: "PS#" may contain volatile content, such as backquote commands.
189//config: This option recreates the prompt string from the environment
190//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200191//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200192
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 */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100403
404static void exitshell(void) NORETURN;
405
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000406/*
Eric Andersen2870d962001-07-02 17:27:21 +0000407 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000408 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000409 * much more efficient and portable. (But hacking the kernel is so much
410 * more fun than worrying about efficiency and portability. :-))
411 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000412#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200413 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000414 xbarrier(); \
415} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000416
417/*
418 * Called to raise an exception. Since C doesn't include exceptions, we
419 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000420 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000421 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000422static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000423static void
424raise_exception(int e)
425{
426#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000427 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000428 abort();
429#endif
430 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000431 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000432 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000433}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000434#if DEBUG
435#define raise_exception(e) do { \
436 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
437 raise_exception(e); \
438} while (0)
439#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000440
441/*
442 * Called from trap.c when a SIGINT is received. (If the user specifies
443 * that SIGINT is to be trapped or ignored using the trap builtin, then
444 * this routine is not called.) Suppressint is nonzero when interrupts
445 * are held using the INT_OFF macro. (The test for iflag is just
446 * defensive programming.)
447 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000448static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000449static void
450raise_interrupt(void)
451{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000452 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000453
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200454 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000455 /* Signal is not automatically unmasked after it is raised,
456 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000457 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200458 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000459
Denis Vlasenko4b875702009-03-19 13:30:04 +0000460 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000461 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
462 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000463 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000464 signal(SIGINT, SIG_DFL);
465 raise(SIGINT);
466 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000467 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000468 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000469 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000470 /* NOTREACHED */
471}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000472#if DEBUG
473#define raise_interrupt() do { \
474 TRACE(("raising interrupt on line %d\n", __LINE__)); \
475 raise_interrupt(); \
476} while (0)
477#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000478
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000479static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000480int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000481{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000482 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200483 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000484 raise_interrupt();
485 }
486}
487#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000488static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000489force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000490{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000491 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200492 suppress_int = 0;
493 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000494 raise_interrupt();
495}
496#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000497
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200498#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000499
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000500#define RESTORE_INT(v) do { \
501 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200502 suppress_int = (v); \
503 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000504 raise_interrupt(); \
505} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000506
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000507
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000508/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000509
Eric Andersenc470f442003-07-28 09:56:35 +0000510static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000511outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000512{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000513 INT_OFF;
514 fputs(p, file);
515 INT_ON;
516}
517
518static void
519flush_stdout_stderr(void)
520{
521 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100522 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000523 INT_ON;
524}
525
526static void
527outcslow(int c, FILE *dest)
528{
529 INT_OFF;
530 putc(c, dest);
531 fflush(dest);
532 INT_ON;
533}
534
535static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
536static int
537out1fmt(const char *fmt, ...)
538{
539 va_list ap;
540 int r;
541
542 INT_OFF;
543 va_start(ap, fmt);
544 r = vprintf(fmt, ap);
545 va_end(ap);
546 INT_ON;
547 return r;
548}
549
550static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
551static int
552fmtstr(char *outbuf, size_t length, const char *fmt, ...)
553{
554 va_list ap;
555 int ret;
556
557 va_start(ap, fmt);
558 INT_OFF;
559 ret = vsnprintf(outbuf, length, fmt, ap);
560 va_end(ap);
561 INT_ON;
562 return ret;
563}
564
565static void
566out1str(const char *p)
567{
568 outstr(p, stdout);
569}
570
571static void
572out2str(const char *p)
573{
574 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100575 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000576}
577
578
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000579/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000580
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000581/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100582#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200583#define CTLESC ((unsigned char)'\201') /* escape next character */
584#define CTLVAR ((unsigned char)'\202') /* variable defn */
585#define CTLENDVAR ((unsigned char)'\203')
586#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000587#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
588/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200589#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
590#define CTLENDARI ((unsigned char)'\207')
591#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100592#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000593
594/* variable substitution byte (follows CTLVAR) */
595#define VSTYPE 0x0f /* type of variable substitution */
596#define VSNUL 0x10 /* colon--treat the empty string as unset */
597#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
598
599/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000600#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
601#define VSMINUS 0x2 /* ${var-text} */
602#define VSPLUS 0x3 /* ${var+text} */
603#define VSQUESTION 0x4 /* ${var?message} */
604#define VSASSIGN 0x5 /* ${var=text} */
605#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
606#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
607#define VSTRIMLEFT 0x8 /* ${var#pattern} */
608#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
609#define VSLENGTH 0xa /* ${#var} */
610#if ENABLE_ASH_BASH_COMPAT
611#define VSSUBSTR 0xc /* ${var:position:length} */
612#define VSREPLACE 0xd /* ${var/pattern/replacement} */
613#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
614#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000615
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000616static const char dolatstr[] ALIGN1 = {
617 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
618};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000619
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620#define NCMD 0
621#define NPIPE 1
622#define NREDIR 2
623#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000624#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000625#define NAND 5
626#define NOR 6
627#define NSEMI 7
628#define NIF 8
629#define NWHILE 9
630#define NUNTIL 10
631#define NFOR 11
632#define NCASE 12
633#define NCLIST 13
634#define NDEFUN 14
635#define NARG 15
636#define NTO 16
637#if ENABLE_ASH_BASH_COMPAT
638#define NTO2 17
639#endif
640#define NCLOBBER 18
641#define NFROM 19
642#define NFROMTO 20
643#define NAPPEND 21
644#define NTOFD 22
645#define NFROMFD 23
646#define NHERE 24
647#define NXHERE 25
648#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000649#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000650
651union node;
652
653struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000654 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000655 union node *assign;
656 union node *args;
657 union node *redirect;
658};
659
660struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000661 smallint type;
662 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000663 struct nodelist *cmdlist;
664};
665
666struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000667 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000668 union node *n;
669 union node *redirect;
670};
671
672struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000673 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000674 union node *ch1;
675 union node *ch2;
676};
677
678struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000679 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000680 union node *test;
681 union node *ifpart;
682 union node *elsepart;
683};
684
685struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000686 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000687 union node *args;
688 union node *body;
689 char *var;
690};
691
692struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000693 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000694 union node *expr;
695 union node *cases;
696};
697
698struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000699 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000700 union node *next;
701 union node *pattern;
702 union node *body;
703};
704
705struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000706 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000707 union node *next;
708 char *text;
709 struct nodelist *backquote;
710};
711
Denis Vlasenko559691a2008-10-05 18:39:31 +0000712/* nfile and ndup layout must match!
713 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
714 * that it is actually NTO2 (>&file), and change its type.
715 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000716struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000717 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000718 union node *next;
719 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000720 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000721 union node *fname;
722 char *expfname;
723};
724
725struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000726 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000727 union node *next;
728 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000729 int dupfd;
730 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000731 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000732};
733
734struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000735 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000736 union node *next;
737 int fd;
738 union node *doc;
739};
740
741struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000742 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000743 union node *com;
744};
745
746union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000747 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000748 struct ncmd ncmd;
749 struct npipe npipe;
750 struct nredir nredir;
751 struct nbinary nbinary;
752 struct nif nif;
753 struct nfor nfor;
754 struct ncase ncase;
755 struct nclist nclist;
756 struct narg narg;
757 struct nfile nfile;
758 struct ndup ndup;
759 struct nhere nhere;
760 struct nnot nnot;
761};
762
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200763/*
764 * NODE_EOF is returned by parsecmd when it encounters an end of file.
765 * It must be distinct from NULL.
766 */
767#define NODE_EOF ((union node *) -1L)
768
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000769struct nodelist {
770 struct nodelist *next;
771 union node *n;
772};
773
774struct funcnode {
775 int count;
776 union node n;
777};
778
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000779/*
780 * Free a parse tree.
781 */
782static void
783freefunc(struct funcnode *f)
784{
785 if (f && --f->count < 0)
786 free(f);
787}
788
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000789
790/* ============ Debugging output */
791
792#if DEBUG
793
794static FILE *tracefile;
795
796static void
797trace_printf(const char *fmt, ...)
798{
799 va_list va;
800
801 if (debug != 1)
802 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000803 if (DEBUG_TIME)
804 fprintf(tracefile, "%u ", (int) time(NULL));
805 if (DEBUG_PID)
806 fprintf(tracefile, "[%u] ", (int) getpid());
807 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200808 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000809 va_start(va, fmt);
810 vfprintf(tracefile, fmt, va);
811 va_end(va);
812}
813
814static void
815trace_vprintf(const char *fmt, va_list va)
816{
817 if (debug != 1)
818 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000819 if (DEBUG_TIME)
820 fprintf(tracefile, "%u ", (int) time(NULL));
821 if (DEBUG_PID)
822 fprintf(tracefile, "[%u] ", (int) getpid());
823 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200824 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000825 vfprintf(tracefile, fmt, va);
826}
827
828static void
829trace_puts(const char *s)
830{
831 if (debug != 1)
832 return;
833 fputs(s, tracefile);
834}
835
836static void
837trace_puts_quoted(char *s)
838{
839 char *p;
840 char c;
841
842 if (debug != 1)
843 return;
844 putc('"', tracefile);
845 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100846 switch ((unsigned char)*p) {
847 case '\n': c = 'n'; goto backslash;
848 case '\t': c = 't'; goto backslash;
849 case '\r': c = 'r'; goto backslash;
850 case '\"': c = '\"'; goto backslash;
851 case '\\': c = '\\'; goto backslash;
852 case CTLESC: c = 'e'; goto backslash;
853 case CTLVAR: c = 'v'; goto backslash;
854 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
855 case CTLBACKQ: c = 'q'; goto backslash;
856 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000857 backslash:
858 putc('\\', tracefile);
859 putc(c, tracefile);
860 break;
861 default:
862 if (*p >= ' ' && *p <= '~')
863 putc(*p, tracefile);
864 else {
865 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100866 putc((*p >> 6) & 03, tracefile);
867 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000868 putc(*p & 07, tracefile);
869 }
870 break;
871 }
872 }
873 putc('"', tracefile);
874}
875
876static void
877trace_puts_args(char **ap)
878{
879 if (debug != 1)
880 return;
881 if (!*ap)
882 return;
883 while (1) {
884 trace_puts_quoted(*ap);
885 if (!*++ap) {
886 putc('\n', tracefile);
887 break;
888 }
889 putc(' ', tracefile);
890 }
891}
892
893static void
894opentrace(void)
895{
896 char s[100];
897#ifdef O_APPEND
898 int flags;
899#endif
900
901 if (debug != 1) {
902 if (tracefile)
903 fflush(tracefile);
904 /* leave open because libedit might be using it */
905 return;
906 }
907 strcpy(s, "./trace");
908 if (tracefile) {
909 if (!freopen(s, "a", tracefile)) {
910 fprintf(stderr, "Can't re-open %s\n", s);
911 debug = 0;
912 return;
913 }
914 } else {
915 tracefile = fopen(s, "a");
916 if (tracefile == NULL) {
917 fprintf(stderr, "Can't open %s\n", s);
918 debug = 0;
919 return;
920 }
921 }
922#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000923 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000924 if (flags >= 0)
925 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
926#endif
927 setlinebuf(tracefile);
928 fputs("\nTracing started.\n", tracefile);
929}
930
931static void
932indent(int amount, char *pfx, FILE *fp)
933{
934 int i;
935
936 for (i = 0; i < amount; i++) {
937 if (pfx && i == amount - 1)
938 fputs(pfx, fp);
939 putc('\t', fp);
940 }
941}
942
943/* little circular references here... */
944static void shtree(union node *n, int ind, char *pfx, FILE *fp);
945
946static void
947sharg(union node *arg, FILE *fp)
948{
949 char *p;
950 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100951 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000952
953 if (arg->type != NARG) {
954 out1fmt("<node type %d>\n", arg->type);
955 abort();
956 }
957 bqlist = arg->narg.backquote;
958 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100959 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000960 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700961 p++;
962 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000963 break;
964 case CTLVAR:
965 putc('$', fp);
966 putc('{', fp);
967 subtype = *++p;
968 if (subtype == VSLENGTH)
969 putc('#', fp);
970
Dan Fandrich77d48722010-09-07 23:38:28 -0700971 while (*p != '=') {
972 putc(*p, fp);
973 p++;
974 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000975
976 if (subtype & VSNUL)
977 putc(':', fp);
978
979 switch (subtype & VSTYPE) {
980 case VSNORMAL:
981 putc('}', fp);
982 break;
983 case VSMINUS:
984 putc('-', fp);
985 break;
986 case VSPLUS:
987 putc('+', fp);
988 break;
989 case VSQUESTION:
990 putc('?', fp);
991 break;
992 case VSASSIGN:
993 putc('=', fp);
994 break;
995 case VSTRIMLEFT:
996 putc('#', fp);
997 break;
998 case VSTRIMLEFTMAX:
999 putc('#', fp);
1000 putc('#', fp);
1001 break;
1002 case VSTRIMRIGHT:
1003 putc('%', fp);
1004 break;
1005 case VSTRIMRIGHTMAX:
1006 putc('%', fp);
1007 putc('%', fp);
1008 break;
1009 case VSLENGTH:
1010 break;
1011 default:
1012 out1fmt("<subtype %d>", subtype);
1013 }
1014 break;
1015 case CTLENDVAR:
1016 putc('}', fp);
1017 break;
1018 case CTLBACKQ:
1019 case CTLBACKQ|CTLQUOTE:
1020 putc('$', fp);
1021 putc('(', fp);
1022 shtree(bqlist->n, -1, NULL, fp);
1023 putc(')', fp);
1024 break;
1025 default:
1026 putc(*p, fp);
1027 break;
1028 }
1029 }
1030}
1031
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001032static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001033shcmd(union node *cmd, FILE *fp)
1034{
1035 union node *np;
1036 int first;
1037 const char *s;
1038 int dftfd;
1039
1040 first = 1;
1041 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001042 if (!first)
1043 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001044 sharg(np, fp);
1045 first = 0;
1046 }
1047 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001048 if (!first)
1049 putc(' ', fp);
1050 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001051 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001052 case NTO: s = ">>"+1; dftfd = 1; break;
1053 case NCLOBBER: s = ">|"; dftfd = 1; break;
1054 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001055#if ENABLE_ASH_BASH_COMPAT
1056 case NTO2:
1057#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001058 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001059 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001060 case NFROMFD: s = "<&"; break;
1061 case NFROMTO: s = "<>"; break;
1062 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001063 }
1064 if (np->nfile.fd != dftfd)
1065 fprintf(fp, "%d", np->nfile.fd);
1066 fputs(s, fp);
1067 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1068 fprintf(fp, "%d", np->ndup.dupfd);
1069 } else {
1070 sharg(np->nfile.fname, fp);
1071 }
1072 first = 0;
1073 }
1074}
1075
1076static void
1077shtree(union node *n, int ind, char *pfx, FILE *fp)
1078{
1079 struct nodelist *lp;
1080 const char *s;
1081
1082 if (n == NULL)
1083 return;
1084
1085 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001086
1087 if (n == NODE_EOF) {
1088 fputs("<EOF>", fp);
1089 return;
1090 }
1091
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001092 switch (n->type) {
1093 case NSEMI:
1094 s = "; ";
1095 goto binop;
1096 case NAND:
1097 s = " && ";
1098 goto binop;
1099 case NOR:
1100 s = " || ";
1101 binop:
1102 shtree(n->nbinary.ch1, ind, NULL, fp);
1103 /* if (ind < 0) */
1104 fputs(s, fp);
1105 shtree(n->nbinary.ch2, ind, NULL, fp);
1106 break;
1107 case NCMD:
1108 shcmd(n, fp);
1109 if (ind >= 0)
1110 putc('\n', fp);
1111 break;
1112 case NPIPE:
1113 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001114 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001115 if (lp->next)
1116 fputs(" | ", fp);
1117 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001118 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001119 fputs(" &", fp);
1120 if (ind >= 0)
1121 putc('\n', fp);
1122 break;
1123 default:
1124 fprintf(fp, "<node type %d>", n->type);
1125 if (ind >= 0)
1126 putc('\n', fp);
1127 break;
1128 }
1129}
1130
1131static void
1132showtree(union node *n)
1133{
1134 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001135 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001136}
1137
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001138#endif /* DEBUG */
1139
1140
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001141/* ============ Parser data */
1142
1143/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001144 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1145 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001146struct strlist {
1147 struct strlist *next;
1148 char *text;
1149};
1150
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001151struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001152
Denis Vlasenkob012b102007-02-19 22:43:01 +00001153struct strpush {
1154 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001155 char *prev_string;
1156 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001157#if ENABLE_ASH_ALIAS
1158 struct alias *ap; /* if push was associated with an alias */
1159#endif
1160 char *string; /* remember the string since it may change */
1161};
1162
1163struct parsefile {
1164 struct parsefile *prev; /* preceding file on stack */
1165 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001166 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001167 int left_in_line; /* number of chars left in this line */
1168 int left_in_buffer; /* number of chars left in this buffer past the line */
1169 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001170 char *buf; /* input buffer */
1171 struct strpush *strpush; /* for pushing strings at this level */
1172 struct strpush basestrpush; /* so pushing one is fast */
1173};
1174
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001175static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001176static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001177static int startlinno; /* line # where last token started */
1178static char *commandname; /* currently executing command */
1179static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001180static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001181
1182
1183/* ============ Message printing */
1184
1185static void
1186ash_vmsg(const char *msg, va_list ap)
1187{
1188 fprintf(stderr, "%s: ", arg0);
1189 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001190 if (strcmp(arg0, commandname))
1191 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001192 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001193 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001194 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001195 vfprintf(stderr, msg, ap);
1196 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001197}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001198
1199/*
1200 * Exverror is called to raise the error exception. If the second argument
1201 * is not NULL then error prints an error message using printf style
1202 * formatting. It then raises the error exception.
1203 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001204static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001205static void
1206ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001207{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001208#if DEBUG
1209 if (msg) {
1210 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1211 TRACEV((msg, ap));
1212 TRACE(("\") pid=%d\n", getpid()));
1213 } else
1214 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1215 if (msg)
1216#endif
1217 ash_vmsg(msg, ap);
1218
1219 flush_stdout_stderr();
1220 raise_exception(cond);
1221 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001222}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001223
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001224static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001225static void
1226ash_msg_and_raise_error(const char *msg, ...)
1227{
1228 va_list ap;
1229
1230 va_start(ap, msg);
1231 ash_vmsg_and_raise(EXERROR, msg, ap);
1232 /* NOTREACHED */
1233 va_end(ap);
1234}
1235
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001236static void raise_error_syntax(const char *) NORETURN;
1237static void
1238raise_error_syntax(const char *msg)
1239{
1240 ash_msg_and_raise_error("syntax error: %s", msg);
1241 /* NOTREACHED */
1242}
1243
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001244static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001245static void
1246ash_msg_and_raise(int cond, const char *msg, ...)
1247{
1248 va_list ap;
1249
1250 va_start(ap, msg);
1251 ash_vmsg_and_raise(cond, msg, ap);
1252 /* NOTREACHED */
1253 va_end(ap);
1254}
1255
1256/*
1257 * error/warning routines for external builtins
1258 */
1259static void
1260ash_msg(const char *fmt, ...)
1261{
1262 va_list ap;
1263
1264 va_start(ap, fmt);
1265 ash_vmsg(fmt, ap);
1266 va_end(ap);
1267}
1268
1269/*
1270 * Return a string describing an error. The returned string may be a
1271 * pointer to a static buffer that will be overwritten on the next call.
1272 * Action describes the operation that got the error.
1273 */
1274static const char *
1275errmsg(int e, const char *em)
1276{
1277 if (e == ENOENT || e == ENOTDIR) {
1278 return em;
1279 }
1280 return strerror(e);
1281}
1282
1283
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001284/* ============ Memory allocation */
1285
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001286#if 0
1287/* I consider these wrappers nearly useless:
1288 * ok, they return you to nearest exception handler, but
1289 * how much memory do you leak in the process, making
1290 * memory starvation worse?
1291 */
1292static void *
1293ckrealloc(void * p, size_t nbytes)
1294{
1295 p = realloc(p, nbytes);
1296 if (!p)
1297 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1298 return p;
1299}
1300
1301static void *
1302ckmalloc(size_t nbytes)
1303{
1304 return ckrealloc(NULL, nbytes);
1305}
1306
1307static void *
1308ckzalloc(size_t nbytes)
1309{
1310 return memset(ckmalloc(nbytes), 0, nbytes);
1311}
1312
1313static char *
1314ckstrdup(const char *s)
1315{
1316 char *p = strdup(s);
1317 if (!p)
1318 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1319 return p;
1320}
1321#else
1322/* Using bbox equivalents. They exit if out of memory */
1323# define ckrealloc xrealloc
1324# define ckmalloc xmalloc
1325# define ckzalloc xzalloc
1326# define ckstrdup xstrdup
1327#endif
1328
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001329/*
1330 * It appears that grabstackstr() will barf with such alignments
1331 * because stalloc() will return a string allocated in a new stackblock.
1332 */
1333#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1334enum {
1335 /* Most machines require the value returned from malloc to be aligned
1336 * in some way. The following macro will get this right
1337 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001338 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001339 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001340 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001341};
1342
1343struct stack_block {
1344 struct stack_block *prev;
1345 char space[MINSIZE];
1346};
1347
1348struct stackmark {
1349 struct stack_block *stackp;
1350 char *stacknxt;
1351 size_t stacknleft;
1352 struct stackmark *marknext;
1353};
1354
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001355
Denis Vlasenko01631112007-12-16 17:20:38 +00001356struct globals_memstack {
1357 struct stack_block *g_stackp; // = &stackbase;
1358 struct stackmark *markp;
1359 char *g_stacknxt; // = stackbase.space;
1360 char *sstrend; // = stackbase.space + MINSIZE;
1361 size_t g_stacknleft; // = MINSIZE;
1362 int herefd; // = -1;
1363 struct stack_block stackbase;
1364};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001365extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1366#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001367#define g_stackp (G_memstack.g_stackp )
1368#define markp (G_memstack.markp )
1369#define g_stacknxt (G_memstack.g_stacknxt )
1370#define sstrend (G_memstack.sstrend )
1371#define g_stacknleft (G_memstack.g_stacknleft)
1372#define herefd (G_memstack.herefd )
1373#define stackbase (G_memstack.stackbase )
1374#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001375 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1376 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001377 g_stackp = &stackbase; \
1378 g_stacknxt = stackbase.space; \
1379 g_stacknleft = MINSIZE; \
1380 sstrend = stackbase.space + MINSIZE; \
1381 herefd = -1; \
1382} while (0)
1383
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001384
Denis Vlasenko01631112007-12-16 17:20:38 +00001385#define stackblock() ((void *)g_stacknxt)
1386#define stackblocksize() g_stacknleft
1387
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001388/*
1389 * Parse trees for commands are allocated in lifo order, so we use a stack
1390 * to make this more efficient, and also to avoid all sorts of exception
1391 * handling code to handle interrupts in the middle of a parse.
1392 *
1393 * The size 504 was chosen because the Ultrix malloc handles that size
1394 * well.
1395 */
1396static void *
1397stalloc(size_t nbytes)
1398{
1399 char *p;
1400 size_t aligned;
1401
1402 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001403 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001404 size_t len;
1405 size_t blocksize;
1406 struct stack_block *sp;
1407
1408 blocksize = aligned;
1409 if (blocksize < MINSIZE)
1410 blocksize = MINSIZE;
1411 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1412 if (len < blocksize)
1413 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1414 INT_OFF;
1415 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001416 sp->prev = g_stackp;
1417 g_stacknxt = sp->space;
1418 g_stacknleft = blocksize;
1419 sstrend = g_stacknxt + blocksize;
1420 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001421 INT_ON;
1422 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001423 p = g_stacknxt;
1424 g_stacknxt += aligned;
1425 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001426 return p;
1427}
1428
Denis Vlasenko597906c2008-02-20 16:38:54 +00001429static void *
1430stzalloc(size_t nbytes)
1431{
1432 return memset(stalloc(nbytes), 0, nbytes);
1433}
1434
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001435static void
1436stunalloc(void *p)
1437{
1438#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001439 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001440 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001441 abort();
1442 }
1443#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001444 g_stacknleft += g_stacknxt - (char *)p;
1445 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001446}
1447
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001448/*
1449 * Like strdup but works with the ash stack.
1450 */
1451static char *
1452ststrdup(const char *p)
1453{
1454 size_t len = strlen(p) + 1;
1455 return memcpy(stalloc(len), p, len);
1456}
1457
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001458static void
1459setstackmark(struct stackmark *mark)
1460{
Denis Vlasenko01631112007-12-16 17:20:38 +00001461 mark->stackp = g_stackp;
1462 mark->stacknxt = g_stacknxt;
1463 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001464 mark->marknext = markp;
1465 markp = mark;
1466}
1467
1468static void
1469popstackmark(struct stackmark *mark)
1470{
1471 struct stack_block *sp;
1472
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001473 if (!mark->stackp)
1474 return;
1475
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001476 INT_OFF;
1477 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001478 while (g_stackp != mark->stackp) {
1479 sp = g_stackp;
1480 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001481 free(sp);
1482 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001483 g_stacknxt = mark->stacknxt;
1484 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001485 sstrend = mark->stacknxt + mark->stacknleft;
1486 INT_ON;
1487}
1488
1489/*
1490 * When the parser reads in a string, it wants to stick the string on the
1491 * stack and only adjust the stack pointer when it knows how big the
1492 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1493 * of space on top of the stack and stackblocklen returns the length of
1494 * this block. Growstackblock will grow this space by at least one byte,
1495 * possibly moving it (like realloc). Grabstackblock actually allocates the
1496 * part of the block that has been used.
1497 */
1498static void
1499growstackblock(void)
1500{
1501 size_t newlen;
1502
Denis Vlasenko01631112007-12-16 17:20:38 +00001503 newlen = g_stacknleft * 2;
1504 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001505 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1506 if (newlen < 128)
1507 newlen += 128;
1508
Denis Vlasenko01631112007-12-16 17:20:38 +00001509 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001510 struct stack_block *oldstackp;
1511 struct stackmark *xmark;
1512 struct stack_block *sp;
1513 struct stack_block *prevstackp;
1514 size_t grosslen;
1515
1516 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001517 oldstackp = g_stackp;
1518 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001519 prevstackp = sp->prev;
1520 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1521 sp = ckrealloc(sp, grosslen);
1522 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001523 g_stackp = sp;
1524 g_stacknxt = sp->space;
1525 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001526 sstrend = sp->space + newlen;
1527
1528 /*
1529 * Stack marks pointing to the start of the old block
1530 * must be relocated to point to the new block
1531 */
1532 xmark = markp;
1533 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001534 xmark->stackp = g_stackp;
1535 xmark->stacknxt = g_stacknxt;
1536 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001537 xmark = xmark->marknext;
1538 }
1539 INT_ON;
1540 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001541 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001542 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001543 char *p = stalloc(newlen);
1544
1545 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001546 g_stacknxt = memcpy(p, oldspace, oldlen);
1547 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001548 }
1549}
1550
1551static void
1552grabstackblock(size_t len)
1553{
1554 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001555 g_stacknxt += len;
1556 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001557}
1558
1559/*
1560 * The following routines are somewhat easier to use than the above.
1561 * The user declares a variable of type STACKSTR, which may be declared
1562 * to be a register. The macro STARTSTACKSTR initializes things. Then
1563 * the user uses the macro STPUTC to add characters to the string. In
1564 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1565 * grown as necessary. When the user is done, she can just leave the
1566 * string there and refer to it using stackblock(). Or she can allocate
1567 * the space for it using grabstackstr(). If it is necessary to allow
1568 * someone else to use the stack temporarily and then continue to grow
1569 * the string, the user should use grabstack to allocate the space, and
1570 * then call ungrabstr(p) to return to the previous mode of operation.
1571 *
1572 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1573 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1574 * is space for at least one character.
1575 */
1576static void *
1577growstackstr(void)
1578{
1579 size_t len = stackblocksize();
1580 if (herefd >= 0 && len >= 1024) {
1581 full_write(herefd, stackblock(), len);
1582 return stackblock();
1583 }
1584 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001585 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001586}
1587
1588/*
1589 * Called from CHECKSTRSPACE.
1590 */
1591static char *
1592makestrspace(size_t newlen, char *p)
1593{
Denis Vlasenko01631112007-12-16 17:20:38 +00001594 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001595 size_t size = stackblocksize();
1596
1597 for (;;) {
1598 size_t nleft;
1599
1600 size = stackblocksize();
1601 nleft = size - len;
1602 if (nleft >= newlen)
1603 break;
1604 growstackblock();
1605 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001606 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001607}
1608
1609static char *
1610stack_nputstr(const char *s, size_t n, char *p)
1611{
1612 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001613 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001614 return p;
1615}
1616
1617static char *
1618stack_putstr(const char *s, char *p)
1619{
1620 return stack_nputstr(s, strlen(s), p);
1621}
1622
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001623static char *
1624_STPUTC(int c, char *p)
1625{
1626 if (p == sstrend)
1627 p = growstackstr();
1628 *p++ = c;
1629 return p;
1630}
1631
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001632#define STARTSTACKSTR(p) ((p) = stackblock())
1633#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001634#define CHECKSTRSPACE(n, p) do { \
1635 char *q = (p); \
1636 size_t l = (n); \
1637 size_t m = sstrend - q; \
1638 if (l > m) \
1639 (p) = makestrspace(l, q); \
1640} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001641#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001642#define STACKSTRNUL(p) do { \
1643 if ((p) == sstrend) \
1644 (p) = growstackstr(); \
1645 *(p) = '\0'; \
1646} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001647#define STUNPUTC(p) (--(p))
1648#define STTOPC(p) ((p)[-1])
1649#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001650
1651#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001652#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001653#define stackstrend() ((void *)sstrend)
1654
1655
1656/* ============ String helpers */
1657
1658/*
1659 * prefix -- see if pfx is a prefix of string.
1660 */
1661static char *
1662prefix(const char *string, const char *pfx)
1663{
1664 while (*pfx) {
1665 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001666 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001667 }
1668 return (char *) string;
1669}
1670
1671/*
1672 * Check for a valid number. This should be elsewhere.
1673 */
1674static int
1675is_number(const char *p)
1676{
1677 do {
1678 if (!isdigit(*p))
1679 return 0;
1680 } while (*++p != '\0');
1681 return 1;
1682}
1683
1684/*
1685 * Convert a string of digits to an integer, printing an error message on
1686 * failure.
1687 */
1688static int
1689number(const char *s)
1690{
1691 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001692 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001693 return atoi(s);
1694}
1695
1696/*
1697 * Produce a possibly single quoted string suitable as input to the shell.
1698 * The return string is allocated on the stack.
1699 */
1700static char *
1701single_quote(const char *s)
1702{
1703 char *p;
1704
1705 STARTSTACKSTR(p);
1706
1707 do {
1708 char *q;
1709 size_t len;
1710
1711 len = strchrnul(s, '\'') - s;
1712
1713 q = p = makestrspace(len + 3, p);
1714
1715 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001716 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001717 *q++ = '\'';
1718 s += len;
1719
1720 STADJUST(q - p, p);
1721
Denys Vlasenkocd716832009-11-28 22:14:02 +01001722 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001724 len = 0;
1725 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726
1727 q = p = makestrspace(len + 3, p);
1728
1729 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001730 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732
1733 STADJUST(q - p, p);
1734 } while (*s);
1735
Denys Vlasenkocd716832009-11-28 22:14:02 +01001736 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001737
1738 return stackblock();
1739}
1740
1741
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001742/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743
1744static char **argptr; /* argument list for builtin commands */
1745static char *optionarg; /* set by nextopt (like getopt) */
1746static char *optptr; /* used by nextopt */
1747
1748/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001749 * XXX - should get rid of. Have all builtins use getopt(3).
1750 * The library getopt must have the BSD extension static variable
1751 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001752 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001753 * Standard option processing (a la getopt) for builtin routines.
1754 * The only argument that is passed to nextopt is the option string;
1755 * the other arguments are unnecessary. It returns the character,
1756 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757 */
1758static int
1759nextopt(const char *optstring)
1760{
1761 char *p;
1762 const char *q;
1763 char c;
1764
1765 p = optptr;
1766 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001767 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001768 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001769 if (p == NULL)
1770 return '\0';
1771 if (*p != '-')
1772 return '\0';
1773 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774 return '\0';
1775 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001776 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001777 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001778 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001779 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001780 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001781 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001782 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001784 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785 if (*++q == ':')
1786 q++;
1787 }
1788 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001789 if (*p == '\0') {
1790 p = *argptr++;
1791 if (p == NULL)
1792 ash_msg_and_raise_error("no arg for -%c option", c);
1793 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001794 optionarg = p;
1795 p = NULL;
1796 }
1797 optptr = p;
1798 return c;
1799}
1800
1801
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001802/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001803
Denis Vlasenko01631112007-12-16 17:20:38 +00001804/*
1805 * The parsefile structure pointed to by the global variable parsefile
1806 * contains information about the current file being read.
1807 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001808struct shparam {
1809 int nparam; /* # of positional parameters (without $0) */
1810#if ENABLE_ASH_GETOPTS
1811 int optind; /* next parameter to be processed by getopts */
1812 int optoff; /* used by getopts */
1813#endif
1814 unsigned char malloced; /* if parameter list dynamically allocated */
1815 char **p; /* parameter list */
1816};
1817
1818/*
1819 * Free the list of positional parameters.
1820 */
1821static void
1822freeparam(volatile struct shparam *param)
1823{
Denis Vlasenko01631112007-12-16 17:20:38 +00001824 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001825 char **ap, **ap1;
1826 ap = ap1 = param->p;
1827 while (*ap)
1828 free(*ap++);
1829 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001830 }
1831}
1832
1833#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001834static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001835#endif
1836
1837struct var {
1838 struct var *next; /* next entry in hash list */
1839 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001840 const char *var_text; /* name=value */
1841 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001842 /* the variable gets set/unset */
1843};
1844
1845struct localvar {
1846 struct localvar *next; /* next local variable in list */
1847 struct var *vp; /* the variable that was made local */
1848 int flags; /* saved flags */
1849 const char *text; /* saved text */
1850};
1851
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001852/* flags */
1853#define VEXPORT 0x01 /* variable is exported */
1854#define VREADONLY 0x02 /* variable cannot be modified */
1855#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1856#define VTEXTFIXED 0x08 /* text is statically allocated */
1857#define VSTACK 0x10 /* text is allocated on the stack */
1858#define VUNSET 0x20 /* the variable is not set */
1859#define VNOFUNC 0x40 /* don't call the callback function */
1860#define VNOSET 0x80 /* do not set variable - just readonly test */
1861#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001862#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001863# define VDYNAMIC 0x200 /* dynamic variable */
1864#else
1865# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001866#endif
1867
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001868
Denis Vlasenko01631112007-12-16 17:20:38 +00001869/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001870#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001871static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001872change_lc_all(const char *value)
1873{
1874 if (value && *value != '\0')
1875 setlocale(LC_ALL, value);
1876}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001877static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001878change_lc_ctype(const char *value)
1879{
1880 if (value && *value != '\0')
1881 setlocale(LC_CTYPE, value);
1882}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001883#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001884#if ENABLE_ASH_MAIL
1885static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001886static void changemail(const char *var_value) FAST_FUNC;
1887#else
1888# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001889#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001890static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001892static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001893#endif
1894
Denis Vlasenko01631112007-12-16 17:20:38 +00001895static const struct {
1896 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001897 const char *var_text;
1898 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001899} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001900 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001901#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001902 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1903 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001904#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001905 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1906 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1907 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1908 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001909#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001910 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
1912#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001913 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001914#endif
1915#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001916 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1917 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001918#endif
1919#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001920 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001921#endif
1922};
1923
Denis Vlasenko0b769642008-07-24 07:54:57 +00001924struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001925
1926struct globals_var {
1927 struct shparam shellparam; /* $@ current positional parameters */
1928 struct redirtab *redirlist;
1929 int g_nullredirs;
1930 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1931 struct var *vartab[VTABSIZE];
1932 struct var varinit[ARRAY_SIZE(varinit_data)];
1933};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001934extern struct globals_var *const ash_ptr_to_globals_var;
1935#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001936#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001937//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001938#define g_nullredirs (G_var.g_nullredirs )
1939#define preverrout_fd (G_var.preverrout_fd)
1940#define vartab (G_var.vartab )
1941#define varinit (G_var.varinit )
1942#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001943 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001944 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1945 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001946 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001947 varinit[i].flags = varinit_data[i].flags; \
1948 varinit[i].var_text = varinit_data[i].var_text; \
1949 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001950 } \
1951} while (0)
1952
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001953#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001954#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001955# define vmail (&vifs)[1]
1956# define vmpath (&vmail)[1]
1957# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001958#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001959# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001960#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001961#define vps1 (&vpath)[1]
1962#define vps2 (&vps1)[1]
1963#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001965# define voptind (&vps4)[1]
1966# if ENABLE_ASH_RANDOM_SUPPORT
1967# define vrandom (&voptind)[1]
1968# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001969#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001970# if ENABLE_ASH_RANDOM_SUPPORT
1971# define vrandom (&vps4)[1]
1972# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001973#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001974
1975/*
1976 * The following macros access the values of the above variables.
1977 * They have to skip over the name. They return the null string
1978 * for unset variables.
1979 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001980#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001981#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001982#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001983# define mailval() (vmail.var_text + 5)
1984# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001985# define mpathset() ((vmpath.flags & VUNSET) == 0)
1986#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001987#define pathval() (vpath.var_text + 5)
1988#define ps1val() (vps1.var_text + 4)
1989#define ps2val() (vps2.var_text + 4)
1990#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001991#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001992# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001993#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001994
Denis Vlasenko01631112007-12-16 17:20:38 +00001995#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001996static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001997getoptsreset(const char *value)
1998{
1999 shellparam.optind = number(value);
2000 shellparam.optoff = -1;
2001}
2002#endif
2003
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002004/* math.h has these, otherwise define our private copies */
2005#if !ENABLE_SH_MATH_SUPPORT
2006#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2007#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002008/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002009 * Return the pointer to the first char which is not part of a legal variable name
2010 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002011 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002012static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002013endofname(const char *name)
2014{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002015 if (!is_name(*name))
2016 return name;
2017 while (*++name) {
2018 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002019 break;
2020 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002021 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002022}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002023#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002024
2025/*
2026 * Compares two strings up to the first = or '\0'. The first
2027 * string must be terminated by '='; the second may be terminated by
2028 * either '=' or '\0'.
2029 */
2030static int
2031varcmp(const char *p, const char *q)
2032{
2033 int c, d;
2034
2035 while ((c = *p) == (d = *q)) {
2036 if (!c || c == '=')
2037 goto out;
2038 p++;
2039 q++;
2040 }
2041 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002042 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002043 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002044 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002045 out:
2046 return c - d;
2047}
2048
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002049/*
2050 * Find the appropriate entry in the hash table from the name.
2051 */
2052static struct var **
2053hashvar(const char *p)
2054{
2055 unsigned hashval;
2056
2057 hashval = ((unsigned char) *p) << 4;
2058 while (*p && *p != '=')
2059 hashval += (unsigned char) *p++;
2060 return &vartab[hashval % VTABSIZE];
2061}
2062
2063static int
2064vpcmp(const void *a, const void *b)
2065{
2066 return varcmp(*(const char **)a, *(const char **)b);
2067}
2068
2069/*
2070 * This routine initializes the builtin variables.
2071 */
2072static void
2073initvar(void)
2074{
2075 struct var *vp;
2076 struct var *end;
2077 struct var **vpp;
2078
2079 /*
2080 * PS1 depends on uid
2081 */
2082#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002083 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002084#else
2085 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002086 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002087#endif
2088 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002089 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002090 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002091 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002092 vp->next = *vpp;
2093 *vpp = vp;
2094 } while (++vp < end);
2095}
2096
2097static struct var **
2098findvar(struct var **vpp, const char *name)
2099{
2100 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002101 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002102 break;
2103 }
2104 }
2105 return vpp;
2106}
2107
2108/*
2109 * Find the value of a variable. Returns NULL if not set.
2110 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002111static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112lookupvar(const char *name)
2113{
2114 struct var *v;
2115
2116 v = *findvar(hashvar(name), name);
2117 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002118#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119 /*
2120 * Dynamic variables are implemented roughly the same way they are
2121 * in bash. Namely, they're "special" so long as they aren't unset.
2122 * As soon as they're unset, they're no longer dynamic, and dynamic
2123 * lookup will no longer happen at that point. -- PFM.
2124 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002125 if (v->flags & VDYNAMIC)
2126 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002127#endif
2128 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002129 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002130 }
2131 return NULL;
2132}
2133
2134/*
2135 * Search the environment of a builtin command.
2136 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002137static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002138bltinlookup(const char *name)
2139{
2140 struct strlist *sp;
2141
2142 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002143 if (varcmp(sp->text, name) == 0)
2144 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002145 }
2146 return lookupvar(name);
2147}
2148
2149/*
2150 * Same as setvar except that the variable and value are passed in
2151 * the first argument as name=value. Since the first argument will
2152 * be actually stored in the table, it should not be a string that
2153 * will go away.
2154 * Called with interrupts off.
2155 */
2156static void
2157setvareq(char *s, int flags)
2158{
2159 struct var *vp, **vpp;
2160
2161 vpp = hashvar(s);
2162 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2163 vp = *findvar(vpp, s);
2164 if (vp) {
2165 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2166 const char *n;
2167
2168 if (flags & VNOSAVE)
2169 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002170 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002171 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2172 }
2173
2174 if (flags & VNOSET)
2175 return;
2176
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002177 if (vp->var_func && !(flags & VNOFUNC))
2178 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002179
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002180 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2181 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002182
2183 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2184 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002185 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186 if (flags & VNOSET)
2187 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002188 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002189 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002190 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002191 *vpp = vp;
2192 }
2193 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2194 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002195 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002196 vp->flags = flags;
2197}
2198
2199/*
2200 * Set the value of a variable. The flags argument is ored with the
2201 * flags of the variable. If val is NULL, the variable is unset.
2202 */
2203static void
2204setvar(const char *name, const char *val, int flags)
2205{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002206 const char *q;
2207 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002208 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002209 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002210 size_t vallen;
2211
2212 q = endofname(name);
2213 p = strchrnul(q, '=');
2214 namelen = p - name;
2215 if (!namelen || p != q)
2216 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2217 vallen = 0;
2218 if (val == NULL) {
2219 flags |= VUNSET;
2220 } else {
2221 vallen = strlen(val);
2222 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002223
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002224 INT_OFF;
2225 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002226 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002227 if (val) {
2228 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002229 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002230 }
2231 *p = '\0';
2232 setvareq(nameeq, flags | VNOSAVE);
2233 INT_ON;
2234}
2235
Denys Vlasenko03dad222010-01-12 23:29:57 +01002236static void FAST_FUNC
2237setvar2(const char *name, const char *val)
2238{
2239 setvar(name, val, 0);
2240}
2241
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002242#if ENABLE_ASH_GETOPTS
2243/*
2244 * Safe version of setvar, returns 1 on success 0 on failure.
2245 */
2246static int
2247setvarsafe(const char *name, const char *val, int flags)
2248{
2249 int err;
2250 volatile int saveint;
2251 struct jmploc *volatile savehandler = exception_handler;
2252 struct jmploc jmploc;
2253
2254 SAVE_INT(saveint);
2255 if (setjmp(jmploc.loc))
2256 err = 1;
2257 else {
2258 exception_handler = &jmploc;
2259 setvar(name, val, flags);
2260 err = 0;
2261 }
2262 exception_handler = savehandler;
2263 RESTORE_INT(saveint);
2264 return err;
2265}
2266#endif
2267
2268/*
2269 * Unset the specified variable.
2270 */
2271static int
2272unsetvar(const char *s)
2273{
2274 struct var **vpp;
2275 struct var *vp;
2276 int retval;
2277
2278 vpp = findvar(hashvar(s), s);
2279 vp = *vpp;
2280 retval = 2;
2281 if (vp) {
2282 int flags = vp->flags;
2283
2284 retval = 1;
2285 if (flags & VREADONLY)
2286 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002287#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002288 vp->flags &= ~VDYNAMIC;
2289#endif
2290 if (flags & VUNSET)
2291 goto ok;
2292 if ((flags & VSTRFIXED) == 0) {
2293 INT_OFF;
2294 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002295 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002296 *vpp = vp->next;
2297 free(vp);
2298 INT_ON;
2299 } else {
2300 setvar(s, 0, 0);
2301 vp->flags &= ~VEXPORT;
2302 }
2303 ok:
2304 retval = 0;
2305 }
2306 out:
2307 return retval;
2308}
2309
2310/*
2311 * Process a linked list of variable assignments.
2312 */
2313static void
2314listsetvar(struct strlist *list_set_var, int flags)
2315{
2316 struct strlist *lp = list_set_var;
2317
2318 if (!lp)
2319 return;
2320 INT_OFF;
2321 do {
2322 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002323 lp = lp->next;
2324 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002325 INT_ON;
2326}
2327
2328/*
2329 * Generate a list of variables satisfying the given conditions.
2330 */
2331static char **
2332listvars(int on, int off, char ***end)
2333{
2334 struct var **vpp;
2335 struct var *vp;
2336 char **ep;
2337 int mask;
2338
2339 STARTSTACKSTR(ep);
2340 vpp = vartab;
2341 mask = on | off;
2342 do {
2343 for (vp = *vpp; vp; vp = vp->next) {
2344 if ((vp->flags & mask) == on) {
2345 if (ep == stackstrend())
2346 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002347 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002348 }
2349 }
2350 } while (++vpp < vartab + VTABSIZE);
2351 if (ep == stackstrend())
2352 ep = growstackstr();
2353 if (end)
2354 *end = ep;
2355 *ep++ = NULL;
2356 return grabstackstr(ep);
2357}
2358
2359
2360/* ============ Path search helper
2361 *
2362 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002363 * of the path before the first call; path_advance will update
2364 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002365 * the possible path expansions in sequence. If an option (indicated by
2366 * a percent sign) appears in the path entry then the global variable
2367 * pathopt will be set to point to it; otherwise pathopt will be set to
2368 * NULL.
2369 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002370static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002371
2372static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002373path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002374{
2375 const char *p;
2376 char *q;
2377 const char *start;
2378 size_t len;
2379
2380 if (*path == NULL)
2381 return NULL;
2382 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002383 for (p = start; *p && *p != ':' && *p != '%'; p++)
2384 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002385 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2386 while (stackblocksize() < len)
2387 growstackblock();
2388 q = stackblock();
2389 if (p != start) {
2390 memcpy(q, start, p - start);
2391 q += p - start;
2392 *q++ = '/';
2393 }
2394 strcpy(q, name);
2395 pathopt = NULL;
2396 if (*p == '%') {
2397 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002398 while (*p && *p != ':')
2399 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002400 }
2401 if (*p == ':')
2402 *path = p + 1;
2403 else
2404 *path = NULL;
2405 return stalloc(len);
2406}
2407
2408
2409/* ============ Prompt */
2410
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002411static smallint doprompt; /* if set, prompt the user */
2412static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002413
2414#if ENABLE_FEATURE_EDITING
2415static line_input_t *line_input_state;
2416static const char *cmdedit_prompt;
2417static void
2418putprompt(const char *s)
2419{
2420 if (ENABLE_ASH_EXPAND_PRMT) {
2421 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002422 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002423 return;
2424 }
2425 cmdedit_prompt = s;
2426}
2427#else
2428static void
2429putprompt(const char *s)
2430{
2431 out2str(s);
2432}
2433#endif
2434
2435#if ENABLE_ASH_EXPAND_PRMT
2436/* expandstr() needs parsing machinery, so it is far away ahead... */
2437static const char *expandstr(const char *ps);
2438#else
2439#define expandstr(s) s
2440#endif
2441
2442static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002443setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002444{
2445 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002446 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2447
2448 if (!do_set)
2449 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002450
2451 needprompt = 0;
2452
2453 switch (whichprompt) {
2454 case 1:
2455 prompt = ps1val();
2456 break;
2457 case 2:
2458 prompt = ps2val();
2459 break;
2460 default: /* 0 */
2461 prompt = nullstr;
2462 }
2463#if ENABLE_ASH_EXPAND_PRMT
2464 setstackmark(&smark);
2465 stalloc(stackblocksize());
2466#endif
2467 putprompt(expandstr(prompt));
2468#if ENABLE_ASH_EXPAND_PRMT
2469 popstackmark(&smark);
2470#endif
2471}
2472
2473
2474/* ============ The cd and pwd commands */
2475
2476#define CD_PHYSICAL 1
2477#define CD_PRINT 2
2478
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002479static int
2480cdopt(void)
2481{
2482 int flags = 0;
2483 int i, j;
2484
2485 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002486 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002487 if (i != j) {
2488 flags ^= CD_PHYSICAL;
2489 j = i;
2490 }
2491 }
2492
2493 return flags;
2494}
2495
2496/*
2497 * Update curdir (the name of the current directory) in response to a
2498 * cd command.
2499 */
2500static const char *
2501updatepwd(const char *dir)
2502{
2503 char *new;
2504 char *p;
2505 char *cdcomppath;
2506 const char *lim;
2507
2508 cdcomppath = ststrdup(dir);
2509 STARTSTACKSTR(new);
2510 if (*dir != '/') {
2511 if (curdir == nullstr)
2512 return 0;
2513 new = stack_putstr(curdir, new);
2514 }
2515 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002516 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002517 if (*dir != '/') {
2518 if (new[-1] != '/')
2519 USTPUTC('/', new);
2520 if (new > lim && *lim == '/')
2521 lim++;
2522 } else {
2523 USTPUTC('/', new);
2524 cdcomppath++;
2525 if (dir[1] == '/' && dir[2] != '/') {
2526 USTPUTC('/', new);
2527 cdcomppath++;
2528 lim++;
2529 }
2530 }
2531 p = strtok(cdcomppath, "/");
2532 while (p) {
2533 switch (*p) {
2534 case '.':
2535 if (p[1] == '.' && p[2] == '\0') {
2536 while (new > lim) {
2537 STUNPUTC(new);
2538 if (new[-1] == '/')
2539 break;
2540 }
2541 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002542 }
2543 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002544 break;
2545 /* fall through */
2546 default:
2547 new = stack_putstr(p, new);
2548 USTPUTC('/', new);
2549 }
2550 p = strtok(0, "/");
2551 }
2552 if (new > lim)
2553 STUNPUTC(new);
2554 *new = 0;
2555 return stackblock();
2556}
2557
2558/*
2559 * Find out what the current directory is. If we already know the current
2560 * directory, this routine returns immediately.
2561 */
2562static char *
2563getpwd(void)
2564{
Denis Vlasenko01631112007-12-16 17:20:38 +00002565 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002566 return dir ? dir : nullstr;
2567}
2568
2569static void
2570setpwd(const char *val, int setold)
2571{
2572 char *oldcur, *dir;
2573
2574 oldcur = dir = curdir;
2575
2576 if (setold) {
2577 setvar("OLDPWD", oldcur, VEXPORT);
2578 }
2579 INT_OFF;
2580 if (physdir != nullstr) {
2581 if (physdir != oldcur)
2582 free(physdir);
2583 physdir = nullstr;
2584 }
2585 if (oldcur == val || !val) {
2586 char *s = getpwd();
2587 physdir = s;
2588 if (!val)
2589 dir = s;
2590 } else
2591 dir = ckstrdup(val);
2592 if (oldcur != dir && oldcur != nullstr) {
2593 free(oldcur);
2594 }
2595 curdir = dir;
2596 INT_ON;
2597 setvar("PWD", dir, VEXPORT);
2598}
2599
2600static void hashcd(void);
2601
2602/*
2603 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2604 * know that the current directory has changed.
2605 */
2606static int
2607docd(const char *dest, int flags)
2608{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002609 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002610 int err;
2611
2612 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2613
2614 INT_OFF;
2615 if (!(flags & CD_PHYSICAL)) {
2616 dir = updatepwd(dest);
2617 if (dir)
2618 dest = dir;
2619 }
2620 err = chdir(dest);
2621 if (err)
2622 goto out;
2623 setpwd(dir, 1);
2624 hashcd();
2625 out:
2626 INT_ON;
2627 return err;
2628}
2629
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002630static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002631cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002632{
2633 const char *dest;
2634 const char *path;
2635 const char *p;
2636 char c;
2637 struct stat statb;
2638 int flags;
2639
2640 flags = cdopt();
2641 dest = *argptr;
2642 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002643 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002644 else if (LONE_DASH(dest)) {
2645 dest = bltinlookup("OLDPWD");
2646 flags |= CD_PRINT;
2647 }
2648 if (!dest)
2649 dest = nullstr;
2650 if (*dest == '/')
2651 goto step7;
2652 if (*dest == '.') {
2653 c = dest[1];
2654 dotdot:
2655 switch (c) {
2656 case '\0':
2657 case '/':
2658 goto step6;
2659 case '.':
2660 c = dest[2];
2661 if (c != '.')
2662 goto dotdot;
2663 }
2664 }
2665 if (!*dest)
2666 dest = ".";
2667 path = bltinlookup("CDPATH");
2668 if (!path) {
2669 step6:
2670 step7:
2671 p = dest;
2672 goto docd;
2673 }
2674 do {
2675 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002676 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002677 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2678 if (c && c != ':')
2679 flags |= CD_PRINT;
2680 docd:
2681 if (!docd(p, flags))
2682 goto out;
2683 break;
2684 }
2685 } while (path);
2686 ash_msg_and_raise_error("can't cd to %s", dest);
2687 /* NOTREACHED */
2688 out:
2689 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002690 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002691 return 0;
2692}
2693
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002694static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002695pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002696{
2697 int flags;
2698 const char *dir = curdir;
2699
2700 flags = cdopt();
2701 if (flags) {
2702 if (physdir == nullstr)
2703 setpwd(dir, 0);
2704 dir = physdir;
2705 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002706 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002707 return 0;
2708}
2709
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002710
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002711/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002712
Denis Vlasenko834dee72008-10-07 09:18:30 +00002713
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002714#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002715
Eric Andersenc470f442003-07-28 09:56:35 +00002716/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002717#define CWORD 0 /* character is nothing special */
2718#define CNL 1 /* newline character */
2719#define CBACK 2 /* a backslash character */
2720#define CSQUOTE 3 /* single quote */
2721#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002722#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002723#define CBQUOTE 6 /* backwards single quote */
2724#define CVAR 7 /* a dollar sign */
2725#define CENDVAR 8 /* a '}' character */
2726#define CLP 9 /* a left paren in arithmetic */
2727#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002728#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002729#define CCTL 12 /* like CWORD, except it must be escaped */
2730#define CSPCL 13 /* these terminate a word */
2731#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002732
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002733#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002734#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002735# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002736#endif
2737
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002738#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002739
Mike Frysinger98c52642009-04-02 10:02:37 +00002740#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002741# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002742#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002743# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002744#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002745static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002746#if ENABLE_ASH_ALIAS
2747 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2748#endif
2749 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2750 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2751 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2752 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2753 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2754 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2755 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2756 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2757 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2758 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2759 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002760#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002761 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2762 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2763 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2764#endif
2765#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002766};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002767/* Constants below must match table above */
2768enum {
2769#if ENABLE_ASH_ALIAS
2770 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2771#endif
2772 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2773 CNL_CNL_CNL_CNL , /* 2 */
2774 CWORD_CCTL_CCTL_CWORD , /* 3 */
2775 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2776 CVAR_CVAR_CWORD_CVAR , /* 5 */
2777 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2778 CSPCL_CWORD_CWORD_CLP , /* 7 */
2779 CSPCL_CWORD_CWORD_CRP , /* 8 */
2780 CBACK_CBACK_CCTL_CBACK , /* 9 */
2781 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2782 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2783 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2784 CWORD_CWORD_CWORD_CWORD , /* 13 */
2785 CCTL_CCTL_CCTL_CCTL , /* 14 */
2786};
Eric Andersen2870d962001-07-02 17:27:21 +00002787
Denys Vlasenkocd716832009-11-28 22:14:02 +01002788/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2789 * caller must ensure proper cast on it if c is *char_ptr!
2790 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002791/* Values for syntax param */
2792#define BASESYNTAX 0 /* not in quotes */
2793#define DQSYNTAX 1 /* in double quotes */
2794#define SQSYNTAX 2 /* in single quotes */
2795#define ARISYNTAX 3 /* in arithmetic */
2796#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002797
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002798#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002799
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002800static int
2801SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002802{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002803 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002804# if ENABLE_ASH_ALIAS
2805 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002806 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2807 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2808 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2809 11, 3 /* "}~" */
2810 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002811# else
2812 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002813 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2814 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2815 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2816 10, 2 /* "}~" */
2817 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002818# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002819 const char *s;
2820 int indx;
2821
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002822 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002823 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002824# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002825 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002826 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002827 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002828# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002829 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002830 /* Cast is purely for paranoia here,
2831 * just in case someone passed signed char to us */
2832 if ((unsigned char)c >= CTL_FIRST
2833 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002834 ) {
2835 return CCTL;
2836 }
2837 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002838 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002839 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002840 indx = syntax_index_table[s - spec_symbls];
2841 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002842 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002843}
2844
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002845#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002846
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002847static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002848 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002849 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2859 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2860 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2882 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2883 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2884 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2886 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2888 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2889 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2890 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2891 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2892 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2894 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2895 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2896 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2897 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2909 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2910 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2911 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2912 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2942 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2943 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2946 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2974 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2975 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2976 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2977 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2978 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2979 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2980 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2981 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2982 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2983 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2984 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2985 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2986 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003105 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003106# if ENABLE_ASH_ALIAS
3107 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3108# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003109};
3110
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003111# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003112
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003113#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003114
Eric Andersen2870d962001-07-02 17:27:21 +00003115
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003116/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003117
Denis Vlasenko131ae172007-02-18 13:00:19 +00003118#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003119
3120#define ALIASINUSE 1
3121#define ALIASDEAD 2
3122
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003123struct alias {
3124 struct alias *next;
3125 char *name;
3126 char *val;
3127 int flag;
3128};
3129
Denis Vlasenko01631112007-12-16 17:20:38 +00003130
3131static struct alias **atab; // [ATABSIZE];
3132#define INIT_G_alias() do { \
3133 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3134} while (0)
3135
Eric Andersen2870d962001-07-02 17:27:21 +00003136
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003137static struct alias **
3138__lookupalias(const char *name) {
3139 unsigned int hashval;
3140 struct alias **app;
3141 const char *p;
3142 unsigned int ch;
3143
3144 p = name;
3145
3146 ch = (unsigned char)*p;
3147 hashval = ch << 4;
3148 while (ch) {
3149 hashval += ch;
3150 ch = (unsigned char)*++p;
3151 }
3152 app = &atab[hashval % ATABSIZE];
3153
3154 for (; *app; app = &(*app)->next) {
3155 if (strcmp(name, (*app)->name) == 0) {
3156 break;
3157 }
3158 }
3159
3160 return app;
3161}
3162
3163static struct alias *
3164lookupalias(const char *name, int check)
3165{
3166 struct alias *ap = *__lookupalias(name);
3167
3168 if (check && ap && (ap->flag & ALIASINUSE))
3169 return NULL;
3170 return ap;
3171}
3172
3173static struct alias *
3174freealias(struct alias *ap)
3175{
3176 struct alias *next;
3177
3178 if (ap->flag & ALIASINUSE) {
3179 ap->flag |= ALIASDEAD;
3180 return ap;
3181 }
3182
3183 next = ap->next;
3184 free(ap->name);
3185 free(ap->val);
3186 free(ap);
3187 return next;
3188}
Eric Andersencb57d552001-06-28 07:25:16 +00003189
Eric Andersenc470f442003-07-28 09:56:35 +00003190static void
3191setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003192{
3193 struct alias *ap, **app;
3194
3195 app = __lookupalias(name);
3196 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003197 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003198 if (ap) {
3199 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003200 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003201 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003202 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003203 ap->flag &= ~ALIASDEAD;
3204 } else {
3205 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003206 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003207 ap->name = ckstrdup(name);
3208 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003209 /*ap->flag = 0; - ckzalloc did it */
3210 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003211 *app = ap;
3212 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003213 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003214}
3215
Eric Andersenc470f442003-07-28 09:56:35 +00003216static int
3217unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003218{
Eric Andersencb57d552001-06-28 07:25:16 +00003219 struct alias **app;
3220
3221 app = __lookupalias(name);
3222
3223 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003224 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003225 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003226 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003227 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003228 }
3229
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003230 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003231}
3232
Eric Andersenc470f442003-07-28 09:56:35 +00003233static void
3234rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003235{
Eric Andersencb57d552001-06-28 07:25:16 +00003236 struct alias *ap, **app;
3237 int i;
3238
Denis Vlasenkob012b102007-02-19 22:43:01 +00003239 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003240 for (i = 0; i < ATABSIZE; i++) {
3241 app = &atab[i];
3242 for (ap = *app; ap; ap = *app) {
3243 *app = freealias(*app);
3244 if (ap == *app) {
3245 app = &ap->next;
3246 }
3247 }
3248 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003249 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003250}
3251
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003252static void
3253printalias(const struct alias *ap)
3254{
3255 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3256}
3257
Eric Andersencb57d552001-06-28 07:25:16 +00003258/*
3259 * TODO - sort output
3260 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003261static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003262aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003263{
3264 char *n, *v;
3265 int ret = 0;
3266 struct alias *ap;
3267
Denis Vlasenko68404f12008-03-17 09:00:54 +00003268 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003269 int i;
3270
Denis Vlasenko68404f12008-03-17 09:00:54 +00003271 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003272 for (ap = atab[i]; ap; ap = ap->next) {
3273 printalias(ap);
3274 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003275 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003276 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003277 }
3278 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003279 v = strchr(n+1, '=');
3280 if (v == NULL) { /* n+1: funny ksh stuff */
3281 ap = *__lookupalias(n);
3282 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003283 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003284 ret = 1;
3285 } else
3286 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003287 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003288 *v++ = '\0';
3289 setalias(n, v);
3290 }
3291 }
3292
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003293 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003294}
3295
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003296static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003297unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003298{
3299 int i;
3300
3301 while ((i = nextopt("a")) != '\0') {
3302 if (i == 'a') {
3303 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003304 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003305 }
3306 }
3307 for (i = 0; *argptr; argptr++) {
3308 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003309 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003310 i = 1;
3311 }
3312 }
3313
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003314 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003315}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003316
Denis Vlasenko131ae172007-02-18 13:00:19 +00003317#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003318
Eric Andersenc470f442003-07-28 09:56:35 +00003319
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320/* ============ jobs.c */
3321
3322/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003323#define FORK_FG 0
3324#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003325#define FORK_NOJOB 2
3326
3327/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003328#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3329#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3330#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332/*
3333 * A job structure contains information about a job. A job is either a
3334 * single process or a set of processes contained in a pipeline. In the
3335 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3336 * array of pids.
3337 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003339 pid_t ps_pid; /* process id */
3340 int ps_status; /* last process status from wait() */
3341 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342};
3343
3344struct job {
3345 struct procstat ps0; /* status of process */
3346 struct procstat *ps; /* status or processes when more than one */
3347#if JOBS
3348 int stopstatus; /* status of a stopped job */
3349#endif
3350 uint32_t
3351 nprocs: 16, /* number of processes */
3352 state: 8,
3353#define JOBRUNNING 0 /* at least one proc running */
3354#define JOBSTOPPED 1 /* all procs are stopped */
3355#define JOBDONE 2 /* all procs are completed */
3356#if JOBS
3357 sigint: 1, /* job was killed by SIGINT */
3358 jobctl: 1, /* job running under job control */
3359#endif
3360 waited: 1, /* true if this entry has been waited for */
3361 used: 1, /* true if this entry is in used */
3362 changed: 1; /* true if status has changed */
3363 struct job *prev_job; /* previous job */
3364};
3365
Denis Vlasenko68404f12008-03-17 09:00:54 +00003366static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003367static int forkshell(struct job *, union node *, int);
3368static int waitforjob(struct job *);
3369
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003370#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003371enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003372#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003374static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003375static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003376#endif
3377
3378/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003379 * Ignore a signal.
3380 */
3381static void
3382ignoresig(int signo)
3383{
3384 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3385 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3386 /* No, need to do it */
3387 signal(signo, SIG_IGN);
3388 }
3389 sigmode[signo - 1] = S_HARD_IGN;
3390}
3391
3392/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003393 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003394 */
3395static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003396signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003397{
3398 gotsig[signo - 1] = 1;
3399
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003400 if (signo == SIGINT && !trap[SIGINT]) {
3401 if (!suppress_int) {
3402 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003403 raise_interrupt(); /* does not return */
3404 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003405 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003406 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003407 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003408 }
3409}
3410
3411/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003412 * Set the signal handler for the specified signal. The routine figures
3413 * out what it should be set to.
3414 */
3415static void
3416setsignal(int signo)
3417{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003418 char *t;
3419 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003420 struct sigaction act;
3421
3422 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003423 new_act = S_DFL;
3424 if (t != NULL) { /* trap for this sig is set */
3425 new_act = S_CATCH;
3426 if (t[0] == '\0') /* trap is "": ignore this sig */
3427 new_act = S_IGN;
3428 }
3429
3430 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003431 switch (signo) {
3432 case SIGINT:
3433 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003434 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003435 break;
3436 case SIGQUIT:
3437#if DEBUG
3438 if (debug)
3439 break;
3440#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003441 /* man bash:
3442 * "In all cases, bash ignores SIGQUIT. Non-builtin
3443 * commands run by bash have signal handlers
3444 * set to the values inherited by the shell
3445 * from its parent". */
3446 new_act = S_IGN;
3447 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003448 case SIGTERM:
3449 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003450 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003451 break;
3452#if JOBS
3453 case SIGTSTP:
3454 case SIGTTOU:
3455 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003456 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003457 break;
3458#endif
3459 }
3460 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003461//TODO: if !rootshell, we reset SIGQUIT to DFL,
3462//whereas we have to restore it to what shell got on entry
3463//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003464
3465 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003466 cur_act = *t;
3467 if (cur_act == 0) {
3468 /* current setting is not yet known */
3469 if (sigaction(signo, NULL, &act)) {
3470 /* pretend it worked; maybe we should give a warning,
3471 * but other shells don't. We don't alter sigmode,
3472 * so we retry every time.
3473 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003474 return;
3475 }
3476 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003477 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003478 if (mflag
3479 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3480 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003481 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003482 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003483 }
3484 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003485 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003486 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003487
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003488 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003489 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003490 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003491 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003492 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3493 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003494 break;
3495 case S_IGN:
3496 act.sa_handler = SIG_IGN;
3497 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003498 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003499 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003500
3501 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003502}
3503
3504/* mode flags for set_curjob */
3505#define CUR_DELETE 2
3506#define CUR_RUNNING 1
3507#define CUR_STOPPED 0
3508
3509/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003510#define DOWAIT_NONBLOCK WNOHANG
3511#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003512
3513#if JOBS
3514/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003515static int initialpgrp; //references:2
3516static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003517#endif
3518/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003519static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003520/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003521static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003522/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003523static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003524/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003525static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003526
3527static void
3528set_curjob(struct job *jp, unsigned mode)
3529{
3530 struct job *jp1;
3531 struct job **jpp, **curp;
3532
3533 /* first remove from list */
3534 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003535 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003536 jp1 = *jpp;
3537 if (jp1 == jp)
3538 break;
3539 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003540 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003541 *jpp = jp1->prev_job;
3542
3543 /* Then re-insert in correct position */
3544 jpp = curp;
3545 switch (mode) {
3546 default:
3547#if DEBUG
3548 abort();
3549#endif
3550 case CUR_DELETE:
3551 /* job being deleted */
3552 break;
3553 case CUR_RUNNING:
3554 /* newly created job or backgrounded job,
3555 put after all stopped jobs. */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003556 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003557 jp1 = *jpp;
3558#if JOBS
3559 if (!jp1 || jp1->state != JOBSTOPPED)
3560#endif
3561 break;
3562 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003563 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003564 /* FALLTHROUGH */
3565#if JOBS
3566 case CUR_STOPPED:
3567#endif
3568 /* newly stopped job - becomes curjob */
3569 jp->prev_job = *jpp;
3570 *jpp = jp;
3571 break;
3572 }
3573}
3574
3575#if JOBS || DEBUG
3576static int
3577jobno(const struct job *jp)
3578{
3579 return jp - jobtab + 1;
3580}
3581#endif
3582
3583/*
3584 * Convert a job name to a job structure.
3585 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003586#if !JOBS
3587#define getjob(name, getctl) getjob(name)
3588#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003589static struct job *
3590getjob(const char *name, int getctl)
3591{
3592 struct job *jp;
3593 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003594 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003595 unsigned num;
3596 int c;
3597 const char *p;
3598 char *(*match)(const char *, const char *);
3599
3600 jp = curjob;
3601 p = name;
3602 if (!p)
3603 goto currentjob;
3604
3605 if (*p != '%')
3606 goto err;
3607
3608 c = *++p;
3609 if (!c)
3610 goto currentjob;
3611
3612 if (!p[1]) {
3613 if (c == '+' || c == '%') {
3614 currentjob:
3615 err_msg = "No current job";
3616 goto check;
3617 }
3618 if (c == '-') {
3619 if (jp)
3620 jp = jp->prev_job;
3621 err_msg = "No previous job";
3622 check:
3623 if (!jp)
3624 goto err;
3625 goto gotit;
3626 }
3627 }
3628
3629 if (is_number(p)) {
3630 num = atoi(p);
3631 if (num < njobs) {
3632 jp = jobtab + num - 1;
3633 if (jp->used)
3634 goto gotit;
3635 goto err;
3636 }
3637 }
3638
3639 match = prefix;
3640 if (*p == '?') {
3641 match = strstr;
3642 p++;
3643 }
3644
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003645 found = NULL;
3646 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003647 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003648 if (found)
3649 goto err;
3650 found = jp;
3651 err_msg = "%s: ambiguous";
3652 }
3653 jp = jp->prev_job;
3654 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003655 if (!found)
3656 goto err;
3657 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003658
3659 gotit:
3660#if JOBS
3661 err_msg = "job %s not created under job control";
3662 if (getctl && jp->jobctl == 0)
3663 goto err;
3664#endif
3665 return jp;
3666 err:
3667 ash_msg_and_raise_error(err_msg, name);
3668}
3669
3670/*
3671 * Mark a job structure as unused.
3672 */
3673static void
3674freejob(struct job *jp)
3675{
3676 struct procstat *ps;
3677 int i;
3678
3679 INT_OFF;
3680 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003681 if (ps->ps_cmd != nullstr)
3682 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003683 }
3684 if (jp->ps != &jp->ps0)
3685 free(jp->ps);
3686 jp->used = 0;
3687 set_curjob(jp, CUR_DELETE);
3688 INT_ON;
3689}
3690
3691#if JOBS
3692static void
3693xtcsetpgrp(int fd, pid_t pgrp)
3694{
3695 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003696 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003697}
3698
3699/*
3700 * Turn job control on and off.
3701 *
3702 * Note: This code assumes that the third arg to ioctl is a character
3703 * pointer, which is true on Berkeley systems but not System V. Since
3704 * System V doesn't have job control yet, this isn't a problem now.
3705 *
3706 * Called with interrupts off.
3707 */
3708static void
3709setjobctl(int on)
3710{
3711 int fd;
3712 int pgrp;
3713
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003714 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003715 return;
3716 if (on) {
3717 int ofd;
3718 ofd = fd = open(_PATH_TTY, O_RDWR);
3719 if (fd < 0) {
3720 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3721 * That sometimes helps to acquire controlling tty.
3722 * Obviously, a workaround for bugs when someone
3723 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003724 fd = 2;
3725 while (!isatty(fd))
3726 if (--fd < 0)
3727 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003728 }
3729 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003730 if (ofd >= 0)
3731 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003732 if (fd < 0)
3733 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003734 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003735 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003736 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003737 pgrp = tcgetpgrp(fd);
3738 if (pgrp < 0) {
3739 out:
3740 ash_msg("can't access tty; job control turned off");
3741 mflag = on = 0;
3742 goto close;
3743 }
3744 if (pgrp == getpgrp())
3745 break;
3746 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003747 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003748 initialpgrp = pgrp;
3749
3750 setsignal(SIGTSTP);
3751 setsignal(SIGTTOU);
3752 setsignal(SIGTTIN);
3753 pgrp = rootpid;
3754 setpgid(0, pgrp);
3755 xtcsetpgrp(fd, pgrp);
3756 } else {
3757 /* turning job control off */
3758 fd = ttyfd;
3759 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003760 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003761 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003762 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003763 setpgid(0, pgrp);
3764 setsignal(SIGTSTP);
3765 setsignal(SIGTTOU);
3766 setsignal(SIGTTIN);
3767 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003768 if (fd >= 0)
3769 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770 fd = -1;
3771 }
3772 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003773 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003774}
3775
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003776static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003777killcmd(int argc, char **argv)
3778{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003779 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003780 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003781 do {
3782 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003783 /*
3784 * "kill %N" - job kill
3785 * Converting to pgrp / pid kill
3786 */
3787 struct job *jp;
3788 char *dst;
3789 int j, n;
3790
3791 jp = getjob(argv[i], 0);
3792 /*
3793 * In jobs started under job control, we signal
3794 * entire process group by kill -PGRP_ID.
3795 * This happens, f.e., in interactive shell.
3796 *
3797 * Otherwise, we signal each child via
3798 * kill PID1 PID2 PID3.
3799 * Testcases:
3800 * sh -c 'sleep 1|sleep 1 & kill %1'
3801 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3802 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3803 */
3804 n = jp->nprocs; /* can't be 0 (I hope) */
3805 if (jp->jobctl)
3806 n = 1;
3807 dst = alloca(n * sizeof(int)*4);
3808 argv[i] = dst;
3809 for (j = 0; j < n; j++) {
3810 struct procstat *ps = &jp->ps[j];
3811 /* Skip non-running and not-stopped members
3812 * (i.e. dead members) of the job
3813 */
3814 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3815 continue;
3816 /*
3817 * kill_main has matching code to expect
3818 * leading space. Needed to not confuse
3819 * negative pids with "kill -SIGNAL_NO" syntax
3820 */
3821 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3822 }
3823 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003824 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003825 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003826 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003827 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003828}
3829
3830static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003831showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003832{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003833 struct procstat *ps;
3834 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003835
Denys Vlasenko285ad152009-12-04 23:02:27 +01003836 psend = jp->ps + jp->nprocs;
3837 for (ps = jp->ps + 1; ps < psend; ps++)
3838 printf(" | %s", ps->ps_cmd);
3839 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840 flush_stdout_stderr();
3841}
3842
3843
3844static int
3845restartjob(struct job *jp, int mode)
3846{
3847 struct procstat *ps;
3848 int i;
3849 int status;
3850 pid_t pgid;
3851
3852 INT_OFF;
3853 if (jp->state == JOBDONE)
3854 goto out;
3855 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003856 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003857 if (mode == FORK_FG)
3858 xtcsetpgrp(ttyfd, pgid);
3859 killpg(pgid, SIGCONT);
3860 ps = jp->ps;
3861 i = jp->nprocs;
3862 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003863 if (WIFSTOPPED(ps->ps_status)) {
3864 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003865 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003866 ps++;
3867 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003868 out:
3869 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3870 INT_ON;
3871 return status;
3872}
3873
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003874static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003875fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003876{
3877 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003878 int mode;
3879 int retval;
3880
3881 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3882 nextopt(nullstr);
3883 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003884 do {
3885 jp = getjob(*argv, 1);
3886 if (mode == FORK_BG) {
3887 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003888 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003890 out1str(jp->ps[0].ps_cmd);
3891 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003892 retval = restartjob(jp, mode);
3893 } while (*argv && *++argv);
3894 return retval;
3895}
3896#endif
3897
3898static int
3899sprint_status(char *s, int status, int sigonly)
3900{
3901 int col;
3902 int st;
3903
3904 col = 0;
3905 if (!WIFEXITED(status)) {
3906#if JOBS
3907 if (WIFSTOPPED(status))
3908 st = WSTOPSIG(status);
3909 else
3910#endif
3911 st = WTERMSIG(status);
3912 if (sigonly) {
3913 if (st == SIGINT || st == SIGPIPE)
3914 goto out;
3915#if JOBS
3916 if (WIFSTOPPED(status))
3917 goto out;
3918#endif
3919 }
3920 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003921//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003922 col = fmtstr(s, 32, strsignal(st));
3923 if (WCOREDUMP(status)) {
3924 col += fmtstr(s + col, 16, " (core dumped)");
3925 }
3926 } else if (!sigonly) {
3927 st = WEXITSTATUS(status);
3928 if (st)
3929 col = fmtstr(s, 16, "Done(%d)", st);
3930 else
3931 col = fmtstr(s, 16, "Done");
3932 }
3933 out:
3934 return col;
3935}
3936
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003937static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003938dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003939{
3940 int pid;
3941 int status;
3942 struct job *jp;
3943 struct job *thisjob;
3944 int state;
3945
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003946 TRACE(("dowait(0x%x) called\n", wait_flags));
3947
3948 /* Do a wait system call. If job control is compiled in, we accept
3949 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3950 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003951 if (doing_jobctl)
3952 wait_flags |= WUNTRACED;
3953 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003954 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3955 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003956 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003958
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003959 INT_OFF;
3960 thisjob = NULL;
3961 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003962 struct procstat *ps;
3963 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964 if (jp->state == JOBDONE)
3965 continue;
3966 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003967 ps = jp->ps;
3968 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003969 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003970 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971 TRACE(("Job %d: changing status of proc %d "
3972 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003973 jobno(jp), pid, ps->ps_status, status));
3974 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003975 thisjob = jp;
3976 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003977 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003978 state = JOBRUNNING;
3979#if JOBS
3980 if (state == JOBRUNNING)
3981 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003982 if (WIFSTOPPED(ps->ps_status)) {
3983 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003984 state = JOBSTOPPED;
3985 }
3986#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003987 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003988 if (thisjob)
3989 goto gotjob;
3990 }
3991#if JOBS
3992 if (!WIFSTOPPED(status))
3993#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003994 jobless--;
3995 goto out;
3996
3997 gotjob:
3998 if (state != JOBRUNNING) {
3999 thisjob->changed = 1;
4000
4001 if (thisjob->state != state) {
4002 TRACE(("Job %d: changing state from %d to %d\n",
4003 jobno(thisjob), thisjob->state, state));
4004 thisjob->state = state;
4005#if JOBS
4006 if (state == JOBSTOPPED) {
4007 set_curjob(thisjob, CUR_STOPPED);
4008 }
4009#endif
4010 }
4011 }
4012
4013 out:
4014 INT_ON;
4015
4016 if (thisjob && thisjob == job) {
4017 char s[48 + 1];
4018 int len;
4019
4020 len = sprint_status(s, status, 1);
4021 if (len) {
4022 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004023 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004024 out2str(s);
4025 }
4026 }
4027 return pid;
4028}
4029
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004030static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004031blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004032{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004033 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004034 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004035 raise_exception(EXSIG);
4036 return pid;
4037}
4038
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004039#if JOBS
4040static void
4041showjob(FILE *out, struct job *jp, int mode)
4042{
4043 struct procstat *ps;
4044 struct procstat *psend;
4045 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004046 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004047 char s[80];
4048
4049 ps = jp->ps;
4050
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004051 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004053 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054 return;
4055 }
4056
4057 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004058 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059
4060 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004061 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004062 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004063 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004065 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004066 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004067
4068 psend = ps + jp->nprocs;
4069
4070 if (jp->state == JOBRUNNING) {
4071 strcpy(s + col, "Running");
4072 col += sizeof("Running") - 1;
4073 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004074 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004075 if (jp->state == JOBSTOPPED)
4076 status = jp->stopstatus;
4077 col += sprint_status(s + col, status, 0);
4078 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004079 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004080
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004081 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4082 * or prints several "PID | <cmdN>" lines,
4083 * depending on SHOW_PIDS bit.
4084 * We do not print status of individual processes
4085 * between PID and <cmdN>. bash does it, but not very well:
4086 * first line shows overall job status, not process status,
4087 * making it impossible to know 1st process status.
4088 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004089 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004090 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004091 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004092 s[0] = '\0';
4093 col = 33;
4094 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004095 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004097 fprintf(out, "%s%*c%s%s",
4098 s,
4099 33 - col >= 0 ? 33 - col : 0, ' ',
4100 ps == jp->ps ? "" : "| ",
4101 ps->ps_cmd
4102 );
4103 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004104 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004105
4106 jp->changed = 0;
4107
4108 if (jp->state == JOBDONE) {
4109 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4110 freejob(jp);
4111 }
4112}
4113
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004114/*
4115 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4116 * statuses have changed since the last call to showjobs.
4117 */
4118static void
4119showjobs(FILE *out, int mode)
4120{
4121 struct job *jp;
4122
Denys Vlasenko883cea42009-07-11 15:31:59 +02004123 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004124
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004125 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004126 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004127 continue;
4128
4129 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004130 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004131 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004132 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004133 }
4134}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004135
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004136static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004137jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004138{
4139 int mode, m;
4140
4141 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004142 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004143 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004144 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004145 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004146 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004147 }
4148
4149 argv = argptr;
4150 if (*argv) {
4151 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004152 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004153 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004154 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004155 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004156 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004157
4158 return 0;
4159}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004160#endif /* JOBS */
4161
Michael Abbott359da5e2009-12-04 23:03:29 +01004162/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004163static int
4164getstatus(struct job *job)
4165{
4166 int status;
4167 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004168 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004169
Michael Abbott359da5e2009-12-04 23:03:29 +01004170 /* Fetch last member's status */
4171 ps = job->ps + job->nprocs - 1;
4172 status = ps->ps_status;
4173 if (pipefail) {
4174 /* "set -o pipefail" mode: use last _nonzero_ status */
4175 while (status == 0 && --ps >= job->ps)
4176 status = ps->ps_status;
4177 }
4178
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179 retval = WEXITSTATUS(status);
4180 if (!WIFEXITED(status)) {
4181#if JOBS
4182 retval = WSTOPSIG(status);
4183 if (!WIFSTOPPED(status))
4184#endif
4185 {
4186 /* XXX: limits number of signals */
4187 retval = WTERMSIG(status);
4188#if JOBS
4189 if (retval == SIGINT)
4190 job->sigint = 1;
4191#endif
4192 }
4193 retval += 128;
4194 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004195 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004196 jobno(job), job->nprocs, status, retval));
4197 return retval;
4198}
4199
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004200static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004201waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004202{
4203 struct job *job;
4204 int retval;
4205 struct job *jp;
4206
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004207 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004208 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004209
4210 nextopt(nullstr);
4211 retval = 0;
4212
4213 argv = argptr;
4214 if (!*argv) {
4215 /* wait for all jobs */
4216 for (;;) {
4217 jp = curjob;
4218 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004219 if (!jp) /* no running procs */
4220 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004221 if (jp->state == JOBRUNNING)
4222 break;
4223 jp->waited = 1;
4224 jp = jp->prev_job;
4225 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004226 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004227 /* man bash:
4228 * "When bash is waiting for an asynchronous command via
4229 * the wait builtin, the reception of a signal for which a trap
4230 * has been set will cause the wait builtin to return immediately
4231 * with an exit status greater than 128, immediately after which
4232 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004233 *
4234 * blocking_wait_with_raise_on_sig raises signal handlers
4235 * if it gets no pid (pid < 0). However,
4236 * if child sends us a signal *and immediately exits*,
4237 * blocking_wait_with_raise_on_sig gets pid > 0
4238 * and does not handle pending_sig. Check this case: */
4239 if (pending_sig)
4240 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004241 }
4242 }
4243
4244 retval = 127;
4245 do {
4246 if (**argv != '%') {
4247 pid_t pid = number(*argv);
4248 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004249 while (1) {
4250 if (!job)
4251 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004252 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004253 break;
4254 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004255 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004256 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004257 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004258 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004259 /* loop until process terminated or stopped */
4260 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004261 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004262 job->waited = 1;
4263 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004264 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004265 } while (*++argv);
4266
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004267 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004268 return retval;
4269}
4270
4271static struct job *
4272growjobtab(void)
4273{
4274 size_t len;
4275 ptrdiff_t offset;
4276 struct job *jp, *jq;
4277
4278 len = njobs * sizeof(*jp);
4279 jq = jobtab;
4280 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4281
4282 offset = (char *)jp - (char *)jq;
4283 if (offset) {
4284 /* Relocate pointers */
4285 size_t l = len;
4286
4287 jq = (struct job *)((char *)jq + l);
4288 while (l) {
4289 l -= sizeof(*jp);
4290 jq--;
4291#define joff(p) ((struct job *)((char *)(p) + l))
4292#define jmove(p) (p) = (void *)((char *)(p) + offset)
4293 if (joff(jp)->ps == &jq->ps0)
4294 jmove(joff(jp)->ps);
4295 if (joff(jp)->prev_job)
4296 jmove(joff(jp)->prev_job);
4297 }
4298 if (curjob)
4299 jmove(curjob);
4300#undef joff
4301#undef jmove
4302 }
4303
4304 njobs += 4;
4305 jobtab = jp;
4306 jp = (struct job *)((char *)jp + len);
4307 jq = jp + 3;
4308 do {
4309 jq->used = 0;
4310 } while (--jq >= jp);
4311 return jp;
4312}
4313
4314/*
4315 * Return a new job structure.
4316 * Called with interrupts off.
4317 */
4318static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004319makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004320{
4321 int i;
4322 struct job *jp;
4323
4324 for (i = njobs, jp = jobtab; ; jp++) {
4325 if (--i < 0) {
4326 jp = growjobtab();
4327 break;
4328 }
4329 if (jp->used == 0)
4330 break;
4331 if (jp->state != JOBDONE || !jp->waited)
4332 continue;
4333#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004334 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004335 continue;
4336#endif
4337 freejob(jp);
4338 break;
4339 }
4340 memset(jp, 0, sizeof(*jp));
4341#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004342 /* jp->jobctl is a bitfield.
4343 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004344 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004345 jp->jobctl = 1;
4346#endif
4347 jp->prev_job = curjob;
4348 curjob = jp;
4349 jp->used = 1;
4350 jp->ps = &jp->ps0;
4351 if (nprocs > 1) {
4352 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4353 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004354 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004355 jobno(jp)));
4356 return jp;
4357}
4358
4359#if JOBS
4360/*
4361 * Return a string identifying a command (to be printed by the
4362 * jobs command).
4363 */
4364static char *cmdnextc;
4365
4366static void
4367cmdputs(const char *s)
4368{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004369 static const char vstype[VSTYPE + 1][3] = {
4370 "", "}", "-", "+", "?", "=",
4371 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004372 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004373 };
4374
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004375 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004376 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004377 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004378 unsigned char c;
4379 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004380 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004381
Denys Vlasenko46a14772009-12-10 21:27:13 +01004382 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004383 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4384 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004385 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004386 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 switch (c) {
4388 case CTLESC:
4389 c = *p++;
4390 break;
4391 case CTLVAR:
4392 subtype = *p++;
4393 if ((subtype & VSTYPE) == VSLENGTH)
4394 str = "${#";
4395 else
4396 str = "${";
4397 if (!(subtype & VSQUOTE) == !(quoted & 1))
4398 goto dostr;
4399 quoted ^= 1;
4400 c = '"';
4401 break;
4402 case CTLENDVAR:
4403 str = "\"}" + !(quoted & 1);
4404 quoted >>= 1;
4405 subtype = 0;
4406 goto dostr;
4407 case CTLBACKQ:
4408 str = "$(...)";
4409 goto dostr;
4410 case CTLBACKQ+CTLQUOTE:
4411 str = "\"$(...)\"";
4412 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004413#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004414 case CTLARI:
4415 str = "$((";
4416 goto dostr;
4417 case CTLENDARI:
4418 str = "))";
4419 goto dostr;
4420#endif
4421 case CTLQUOTEMARK:
4422 quoted ^= 1;
4423 c = '"';
4424 break;
4425 case '=':
4426 if (subtype == 0)
4427 break;
4428 if ((subtype & VSTYPE) != VSNORMAL)
4429 quoted <<= 1;
4430 str = vstype[subtype & VSTYPE];
4431 if (subtype & VSNUL)
4432 c = ':';
4433 else
4434 goto checkstr;
4435 break;
4436 case '\'':
4437 case '\\':
4438 case '"':
4439 case '$':
4440 /* These can only happen inside quotes */
4441 cc[0] = c;
4442 str = cc;
4443 c = '\\';
4444 break;
4445 default:
4446 break;
4447 }
4448 USTPUTC(c, nextc);
4449 checkstr:
4450 if (!str)
4451 continue;
4452 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004453 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004454 USTPUTC(c, nextc);
4455 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004456 } /* while *p++ not NUL */
4457
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004458 if (quoted & 1) {
4459 USTPUTC('"', nextc);
4460 }
4461 *nextc = 0;
4462 cmdnextc = nextc;
4463}
4464
4465/* cmdtxt() and cmdlist() call each other */
4466static void cmdtxt(union node *n);
4467
4468static void
4469cmdlist(union node *np, int sep)
4470{
4471 for (; np; np = np->narg.next) {
4472 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004473 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004474 cmdtxt(np);
4475 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004476 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004477 }
4478}
4479
4480static void
4481cmdtxt(union node *n)
4482{
4483 union node *np;
4484 struct nodelist *lp;
4485 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004486
4487 if (!n)
4488 return;
4489 switch (n->type) {
4490 default:
4491#if DEBUG
4492 abort();
4493#endif
4494 case NPIPE:
4495 lp = n->npipe.cmdlist;
4496 for (;;) {
4497 cmdtxt(lp->n);
4498 lp = lp->next;
4499 if (!lp)
4500 break;
4501 cmdputs(" | ");
4502 }
4503 break;
4504 case NSEMI:
4505 p = "; ";
4506 goto binop;
4507 case NAND:
4508 p = " && ";
4509 goto binop;
4510 case NOR:
4511 p = " || ";
4512 binop:
4513 cmdtxt(n->nbinary.ch1);
4514 cmdputs(p);
4515 n = n->nbinary.ch2;
4516 goto donode;
4517 case NREDIR:
4518 case NBACKGND:
4519 n = n->nredir.n;
4520 goto donode;
4521 case NNOT:
4522 cmdputs("!");
4523 n = n->nnot.com;
4524 donode:
4525 cmdtxt(n);
4526 break;
4527 case NIF:
4528 cmdputs("if ");
4529 cmdtxt(n->nif.test);
4530 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004531 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004532 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004533 cmdputs("; else ");
4534 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004535 } else {
4536 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537 }
4538 p = "; fi";
4539 goto dotail;
4540 case NSUBSHELL:
4541 cmdputs("(");
4542 n = n->nredir.n;
4543 p = ")";
4544 goto dotail;
4545 case NWHILE:
4546 p = "while ";
4547 goto until;
4548 case NUNTIL:
4549 p = "until ";
4550 until:
4551 cmdputs(p);
4552 cmdtxt(n->nbinary.ch1);
4553 n = n->nbinary.ch2;
4554 p = "; done";
4555 dodo:
4556 cmdputs("; do ");
4557 dotail:
4558 cmdtxt(n);
4559 goto dotail2;
4560 case NFOR:
4561 cmdputs("for ");
4562 cmdputs(n->nfor.var);
4563 cmdputs(" in ");
4564 cmdlist(n->nfor.args, 1);
4565 n = n->nfor.body;
4566 p = "; done";
4567 goto dodo;
4568 case NDEFUN:
4569 cmdputs(n->narg.text);
4570 p = "() { ... }";
4571 goto dotail2;
4572 case NCMD:
4573 cmdlist(n->ncmd.args, 1);
4574 cmdlist(n->ncmd.redirect, 0);
4575 break;
4576 case NARG:
4577 p = n->narg.text;
4578 dotail2:
4579 cmdputs(p);
4580 break;
4581 case NHERE:
4582 case NXHERE:
4583 p = "<<...";
4584 goto dotail2;
4585 case NCASE:
4586 cmdputs("case ");
4587 cmdputs(n->ncase.expr->narg.text);
4588 cmdputs(" in ");
4589 for (np = n->ncase.cases; np; np = np->nclist.next) {
4590 cmdtxt(np->nclist.pattern);
4591 cmdputs(") ");
4592 cmdtxt(np->nclist.body);
4593 cmdputs(";; ");
4594 }
4595 p = "esac";
4596 goto dotail2;
4597 case NTO:
4598 p = ">";
4599 goto redir;
4600 case NCLOBBER:
4601 p = ">|";
4602 goto redir;
4603 case NAPPEND:
4604 p = ">>";
4605 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004606#if ENABLE_ASH_BASH_COMPAT
4607 case NTO2:
4608#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004609 case NTOFD:
4610 p = ">&";
4611 goto redir;
4612 case NFROM:
4613 p = "<";
4614 goto redir;
4615 case NFROMFD:
4616 p = "<&";
4617 goto redir;
4618 case NFROMTO:
4619 p = "<>";
4620 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004621 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004622 cmdputs(p);
4623 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004624 cmdputs(utoa(n->ndup.dupfd));
4625 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004626 }
4627 n = n->nfile.fname;
4628 goto donode;
4629 }
4630}
4631
4632static char *
4633commandtext(union node *n)
4634{
4635 char *name;
4636
4637 STARTSTACKSTR(cmdnextc);
4638 cmdtxt(n);
4639 name = stackblock();
4640 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4641 name, cmdnextc, cmdnextc));
4642 return ckstrdup(name);
4643}
4644#endif /* JOBS */
4645
4646/*
4647 * Fork off a subshell. If we are doing job control, give the subshell its
4648 * own process group. Jp is a job structure that the job is to be added to.
4649 * N is the command that will be evaluated by the child. Both jp and n may
4650 * be NULL. The mode parameter can be one of the following:
4651 * FORK_FG - Fork off a foreground process.
4652 * FORK_BG - Fork off a background process.
4653 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4654 * process group even if job control is on.
4655 *
4656 * When job control is turned off, background processes have their standard
4657 * input redirected to /dev/null (except for the second and later processes
4658 * in a pipeline).
4659 *
4660 * Called with interrupts off.
4661 */
4662/*
4663 * Clear traps on a fork.
4664 */
4665static void
4666clear_traps(void)
4667{
4668 char **tp;
4669
4670 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004671 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004672 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004673 if (trap_ptr == trap)
4674 free(*tp);
4675 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004676 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004677 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004678 setsignal(tp - trap);
4679 INT_ON;
4680 }
4681 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004682 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004683}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004684
4685/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004686static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004687
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004688/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004689static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004690forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004691{
4692 int oldlvl;
4693
4694 TRACE(("Child shell %d\n", getpid()));
4695 oldlvl = shlvl;
4696 shlvl++;
4697
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004698 /* man bash: "Non-builtin commands run by bash have signal handlers
4699 * set to the values inherited by the shell from its parent".
4700 * Do we do it correctly? */
4701
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004702 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004703
4704 if (mode == FORK_NOJOB /* is it `xxx` ? */
4705 && n && n->type == NCMD /* is it single cmd? */
4706 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004707 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004708 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4709 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4710 ) {
4711 TRACE(("Trap hack\n"));
4712 /* Awful hack for `trap` or $(trap).
4713 *
4714 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4715 * contains an example where "trap" is executed in a subshell:
4716 *
4717 * save_traps=$(trap)
4718 * ...
4719 * eval "$save_traps"
4720 *
4721 * Standard does not say that "trap" in subshell shall print
4722 * parent shell's traps. It only says that its output
4723 * must have suitable form, but then, in the above example
4724 * (which is not supposed to be normative), it implies that.
4725 *
4726 * bash (and probably other shell) does implement it
4727 * (traps are reset to defaults, but "trap" still shows them),
4728 * but as a result, "trap" logic is hopelessly messed up:
4729 *
4730 * # trap
4731 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4732 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4733 * # true | trap <--- trap is in subshell - no output (ditto)
4734 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4735 * trap -- 'echo Ho' SIGWINCH
4736 * # echo `(trap)` <--- in subshell in subshell - output
4737 * trap -- 'echo Ho' SIGWINCH
4738 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4739 * trap -- 'echo Ho' SIGWINCH
4740 *
4741 * The rules when to forget and when to not forget traps
4742 * get really complex and nonsensical.
4743 *
4744 * Our solution: ONLY bare $(trap) or `trap` is special.
4745 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004746 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004747 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004748 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004749 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004750 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004751#if JOBS
4752 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004753 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004754 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004755 pid_t pgrp;
4756
4757 if (jp->nprocs == 0)
4758 pgrp = getpid();
4759 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004760 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004761 /* this can fail because we are doing it in the parent also */
4762 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004763 if (mode == FORK_FG)
4764 xtcsetpgrp(ttyfd, pgrp);
4765 setsignal(SIGTSTP);
4766 setsignal(SIGTTOU);
4767 } else
4768#endif
4769 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004770 /* man bash: "When job control is not in effect,
4771 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004772 ignoresig(SIGINT);
4773 ignoresig(SIGQUIT);
4774 if (jp->nprocs == 0) {
4775 close(0);
4776 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004777 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004778 }
4779 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004780 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004781 if (iflag) { /* why if iflag only? */
4782 setsignal(SIGINT);
4783 setsignal(SIGTERM);
4784 }
4785 /* man bash:
4786 * "In all cases, bash ignores SIGQUIT. Non-builtin
4787 * commands run by bash have signal handlers
4788 * set to the values inherited by the shell
4789 * from its parent".
4790 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004791 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004792 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004793#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004794 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004795 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004796 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004797 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004798 /* "jobs": we do not want to clear job list for it,
4799 * instead we remove only _its_ own_ job from job list.
4800 * This makes "jobs .... | cat" more useful.
4801 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004802 freejob(curjob);
4803 return;
4804 }
4805#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004806 for (jp = curjob; jp; jp = jp->prev_job)
4807 freejob(jp);
4808 jobless = 0;
4809}
4810
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004811/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004812#if !JOBS
4813#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4814#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004815static void
4816forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4817{
4818 TRACE(("In parent shell: child = %d\n", pid));
4819 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004820 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4821 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004822 jobless++;
4823 return;
4824 }
4825#if JOBS
4826 if (mode != FORK_NOJOB && jp->jobctl) {
4827 int pgrp;
4828
4829 if (jp->nprocs == 0)
4830 pgrp = pid;
4831 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004832 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004833 /* This can fail because we are doing it in the child also */
4834 setpgid(pid, pgrp);
4835 }
4836#endif
4837 if (mode == FORK_BG) {
4838 backgndpid = pid; /* set $! */
4839 set_curjob(jp, CUR_RUNNING);
4840 }
4841 if (jp) {
4842 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004843 ps->ps_pid = pid;
4844 ps->ps_status = -1;
4845 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004846#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004847 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004848 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004849#endif
4850 }
4851}
4852
4853static int
4854forkshell(struct job *jp, union node *n, int mode)
4855{
4856 int pid;
4857
4858 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4859 pid = fork();
4860 if (pid < 0) {
4861 TRACE(("Fork failed, errno=%d", errno));
4862 if (jp)
4863 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004864 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004865 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004866 if (pid == 0) {
4867 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004868 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004869 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004870 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004871 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004872 return pid;
4873}
4874
4875/*
4876 * Wait for job to finish.
4877 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004878 * Under job control we have the problem that while a child process
4879 * is running interrupts generated by the user are sent to the child
4880 * but not to the shell. This means that an infinite loop started by
4881 * an interactive user may be hard to kill. With job control turned off,
4882 * an interactive user may place an interactive program inside a loop.
4883 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004884 * these interrupts to also abort the loop. The approach we take here
4885 * is to have the shell ignore interrupt signals while waiting for a
4886 * foreground process to terminate, and then send itself an interrupt
4887 * signal if the child process was terminated by an interrupt signal.
4888 * Unfortunately, some programs want to do a bit of cleanup and then
4889 * exit on interrupt; unless these processes terminate themselves by
4890 * sending a signal to themselves (instead of calling exit) they will
4891 * confuse this approach.
4892 *
4893 * Called with interrupts off.
4894 */
4895static int
4896waitforjob(struct job *jp)
4897{
4898 int st;
4899
4900 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004901
4902 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004903 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004904 /* In non-interactive shells, we _can_ get
4905 * a keyboard signal here and be EINTRed,
4906 * but we just loop back, waiting for command to complete.
4907 *
4908 * man bash:
4909 * "If bash is waiting for a command to complete and receives
4910 * a signal for which a trap has been set, the trap
4911 * will not be executed until the command completes."
4912 *
4913 * Reality is that even if trap is not set, bash
4914 * will not act on the signal until command completes.
4915 * Try this. sleep5intoff.c:
4916 * #include <signal.h>
4917 * #include <unistd.h>
4918 * int main() {
4919 * sigset_t set;
4920 * sigemptyset(&set);
4921 * sigaddset(&set, SIGINT);
4922 * sigaddset(&set, SIGQUIT);
4923 * sigprocmask(SIG_BLOCK, &set, NULL);
4924 * sleep(5);
4925 * return 0;
4926 * }
4927 * $ bash -c './sleep5intoff; echo hi'
4928 * ^C^C^C^C <--- pressing ^C once a second
4929 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004930 * $ bash -c './sleep5intoff; echo hi'
4931 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4932 * $ _
4933 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004934 dowait(DOWAIT_BLOCK, jp);
4935 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004936 INT_ON;
4937
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004938 st = getstatus(jp);
4939#if JOBS
4940 if (jp->jobctl) {
4941 xtcsetpgrp(ttyfd, rootpid);
4942 /*
4943 * This is truly gross.
4944 * If we're doing job control, then we did a TIOCSPGRP which
4945 * caused us (the shell) to no longer be in the controlling
4946 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4947 * intuit from the subprocess exit status whether a SIGINT
4948 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4949 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004950 if (jp->sigint) /* TODO: do the same with all signals */
4951 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004952 }
4953 if (jp->state == JOBDONE)
4954#endif
4955 freejob(jp);
4956 return st;
4957}
4958
4959/*
4960 * return 1 if there are stopped jobs, otherwise 0
4961 */
4962static int
4963stoppedjobs(void)
4964{
4965 struct job *jp;
4966 int retval;
4967
4968 retval = 0;
4969 if (job_warning)
4970 goto out;
4971 jp = curjob;
4972 if (jp && jp->state == JOBSTOPPED) {
4973 out2str("You have stopped jobs.\n");
4974 job_warning = 2;
4975 retval++;
4976 }
4977 out:
4978 return retval;
4979}
4980
4981
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004982/* ============ redir.c
4983 *
4984 * Code for dealing with input/output redirection.
4985 */
4986
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004987#undef EMPTY
4988#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004989#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004990#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004991
4992/*
4993 * Open a file in noclobber mode.
4994 * The code was copied from bash.
4995 */
4996static int
4997noclobberopen(const char *fname)
4998{
4999 int r, fd;
5000 struct stat finfo, finfo2;
5001
5002 /*
5003 * If the file exists and is a regular file, return an error
5004 * immediately.
5005 */
5006 r = stat(fname, &finfo);
5007 if (r == 0 && S_ISREG(finfo.st_mode)) {
5008 errno = EEXIST;
5009 return -1;
5010 }
5011
5012 /*
5013 * If the file was not present (r != 0), make sure we open it
5014 * exclusively so that if it is created before we open it, our open
5015 * will fail. Make sure that we do not truncate an existing file.
5016 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5017 * file was not a regular file, we leave O_EXCL off.
5018 */
5019 if (r != 0)
5020 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5021 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5022
5023 /* If the open failed, return the file descriptor right away. */
5024 if (fd < 0)
5025 return fd;
5026
5027 /*
5028 * OK, the open succeeded, but the file may have been changed from a
5029 * non-regular file to a regular file between the stat and the open.
5030 * We are assuming that the O_EXCL open handles the case where FILENAME
5031 * did not exist and is symlinked to an existing file between the stat
5032 * and open.
5033 */
5034
5035 /*
5036 * If we can open it and fstat the file descriptor, and neither check
5037 * revealed that it was a regular file, and the file has not been
5038 * replaced, return the file descriptor.
5039 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005040 if (fstat(fd, &finfo2) == 0
5041 && !S_ISREG(finfo2.st_mode)
5042 && finfo.st_dev == finfo2.st_dev
5043 && finfo.st_ino == finfo2.st_ino
5044 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005046 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005047
5048 /* The file has been replaced. badness. */
5049 close(fd);
5050 errno = EEXIST;
5051 return -1;
5052}
5053
5054/*
5055 * Handle here documents. Normally we fork off a process to write the
5056 * data to a pipe. If the document is short, we can stuff the data in
5057 * the pipe without forking.
5058 */
5059/* openhere needs this forward reference */
5060static void expandhere(union node *arg, int fd);
5061static int
5062openhere(union node *redir)
5063{
5064 int pip[2];
5065 size_t len = 0;
5066
5067 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005068 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005069 if (redir->type == NHERE) {
5070 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005071 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005072 full_write(pip[1], redir->nhere.doc->narg.text, len);
5073 goto out;
5074 }
5075 }
5076 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005077 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005078 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005079 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5080 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5081 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5082 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005083 signal(SIGPIPE, SIG_DFL);
5084 if (redir->type == NHERE)
5085 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005086 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005087 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005088 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005089 }
5090 out:
5091 close(pip[1]);
5092 return pip[0];
5093}
5094
5095static int
5096openredirect(union node *redir)
5097{
5098 char *fname;
5099 int f;
5100
5101 switch (redir->nfile.type) {
5102 case NFROM:
5103 fname = redir->nfile.expfname;
5104 f = open(fname, O_RDONLY);
5105 if (f < 0)
5106 goto eopen;
5107 break;
5108 case NFROMTO:
5109 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005110 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005111 if (f < 0)
5112 goto ecreate;
5113 break;
5114 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005115#if ENABLE_ASH_BASH_COMPAT
5116 case NTO2:
5117#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005118 /* Take care of noclobber mode. */
5119 if (Cflag) {
5120 fname = redir->nfile.expfname;
5121 f = noclobberopen(fname);
5122 if (f < 0)
5123 goto ecreate;
5124 break;
5125 }
5126 /* FALLTHROUGH */
5127 case NCLOBBER:
5128 fname = redir->nfile.expfname;
5129 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5130 if (f < 0)
5131 goto ecreate;
5132 break;
5133 case NAPPEND:
5134 fname = redir->nfile.expfname;
5135 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5136 if (f < 0)
5137 goto ecreate;
5138 break;
5139 default:
5140#if DEBUG
5141 abort();
5142#endif
5143 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005144/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005145// case NTOFD:
5146// case NFROMFD:
5147// f = -1;
5148// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005149 case NHERE:
5150 case NXHERE:
5151 f = openhere(redir);
5152 break;
5153 }
5154
5155 return f;
5156 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005157 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005158 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005159 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160}
5161
5162/*
5163 * Copy a file descriptor to be >= to. Returns -1
5164 * if the source file descriptor is closed, EMPTY if there are no unused
5165 * file descriptors left.
5166 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005167/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5168 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005169enum {
5170 COPYFD_EXACT = (int)~(INT_MAX),
5171 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5172};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005173static int
5174copyfd(int from, int to)
5175{
5176 int newfd;
5177
Denis Vlasenko5a867312008-07-24 19:46:38 +00005178 if (to & COPYFD_EXACT) {
5179 to &= ~COPYFD_EXACT;
5180 /*if (from != to)*/
5181 newfd = dup2(from, to);
5182 } else {
5183 newfd = fcntl(from, F_DUPFD, to);
5184 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005185 if (newfd < 0) {
5186 if (errno == EMFILE)
5187 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005188 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005189 ash_msg_and_raise_error("%d: %m", from);
5190 }
5191 return newfd;
5192}
5193
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005194/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005195struct two_fd_t {
5196 int orig, copy;
5197};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005198struct redirtab {
5199 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005200 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005201 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005202 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005203};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005204#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005205
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005206static int need_to_remember(struct redirtab *rp, int fd)
5207{
5208 int i;
5209
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005210 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005211 return 0;
5212
5213 for (i = 0; i < rp->pair_count; i++) {
5214 if (rp->two_fd[i].orig == fd) {
5215 /* already remembered */
5216 return 0;
5217 }
5218 }
5219 return 1;
5220}
5221
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005222/* "hidden" fd is a fd used to read scripts, or a copy of such */
5223static int is_hidden_fd(struct redirtab *rp, int fd)
5224{
5225 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005226 struct parsefile *pf;
5227
5228 if (fd == -1)
5229 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005230 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005231 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005232 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005233 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005234 * $ ash # running ash interactively
5235 * $ . ./script.sh
5236 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005237 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005238 * it's still ok to use it: "read" builtin uses it,
5239 * why should we cripple "exec" builtin?
5240 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005241 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005242 return 1;
5243 }
5244 pf = pf->prev;
5245 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005246
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005247 if (!rp)
5248 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005249 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005250 fd |= COPYFD_RESTORE;
5251 for (i = 0; i < rp->pair_count; i++) {
5252 if (rp->two_fd[i].copy == fd) {
5253 return 1;
5254 }
5255 }
5256 return 0;
5257}
5258
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005259/*
5260 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5261 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005262 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005263 */
5264/* flags passed to redirect */
5265#define REDIR_PUSH 01 /* save previous values of file descriptors */
5266#define REDIR_SAVEFD2 03 /* set preverrout */
5267static void
5268redirect(union node *redir, int flags)
5269{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005270 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005271 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005272 int i;
5273 int fd;
5274 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005275 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005276
Denis Vlasenko01631112007-12-16 17:20:38 +00005277 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005278 if (!redir) {
5279 return;
5280 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005281
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005282 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005283 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005284 INT_OFF;
5285 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005286 union node *tmp = redir;
5287 do {
5288 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005289#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005290 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005291 sv_pos++;
5292#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005293 tmp = tmp->nfile.next;
5294 } while (tmp);
5295 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005296 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005297 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005298 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005299 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005300 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005301 while (sv_pos > 0) {
5302 sv_pos--;
5303 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5304 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005305 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005306
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005307 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005308 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005309 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005310 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005311 right_fd = redir->ndup.dupfd;
5312 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005313 /* redirect from/to same file descriptor? */
5314 if (right_fd == fd)
5315 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005316 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005317 if (is_hidden_fd(sv, right_fd)) {
5318 errno = EBADF; /* as if it is closed */
5319 ash_msg_and_raise_error("%d: %m", right_fd);
5320 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005321 newfd = -1;
5322 } else {
5323 newfd = openredirect(redir); /* always >= 0 */
5324 if (fd == newfd) {
5325 /* Descriptor wasn't open before redirect.
5326 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005327 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005328 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005329 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005330 continue;
5331 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005332 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005333#if ENABLE_ASH_BASH_COMPAT
5334 redirect_more:
5335#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005336 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005337 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005338 /* Careful to not accidentally "save"
5339 * to the same fd as right side fd in N>&M */
5340 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5341 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005342/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5343 * are closed in popredir() in the child, preventing them from leaking
5344 * into child. (popredir() also cleans up the mess in case of failures)
5345 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005346 if (i == -1) {
5347 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005348 if (i != EBADF) {
5349 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005350 if (newfd >= 0)
5351 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005352 errno = i;
5353 ash_msg_and_raise_error("%d: %m", fd);
5354 /* NOTREACHED */
5355 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005356 /* EBADF: it is not open - good, remember to close it */
5357 remember_to_close:
5358 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005359 } else { /* fd is open, save its copy */
5360 /* "exec fd>&-" should not close fds
5361 * which point to script file(s).
5362 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005363 if (is_hidden_fd(sv, fd))
5364 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005365 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005366 if (fd == 2)
5367 copied_fd2 = i;
5368 sv->two_fd[sv_pos].orig = fd;
5369 sv->two_fd[sv_pos].copy = i;
5370 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005371 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005372 if (newfd < 0) {
5373 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005374 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005375 /* Don't want to trigger debugging */
5376 if (fd != -1)
5377 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005378 } else {
5379 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005380 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005381 } else if (fd != newfd) { /* move newfd to fd */
5382 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005383#if ENABLE_ASH_BASH_COMPAT
5384 if (!(redir->nfile.type == NTO2 && fd == 2))
5385#endif
5386 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005387 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005388#if ENABLE_ASH_BASH_COMPAT
5389 if (redir->nfile.type == NTO2 && fd == 1) {
5390 /* We already redirected it to fd 1, now copy it to 2 */
5391 newfd = 1;
5392 fd = 2;
5393 goto redirect_more;
5394 }
5395#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005396 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005397
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005398 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005399 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5400 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005401}
5402
5403/*
5404 * Undo the effects of the last redirection.
5405 */
5406static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005407popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005408{
5409 struct redirtab *rp;
5410 int i;
5411
Denis Vlasenko01631112007-12-16 17:20:38 +00005412 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005413 return;
5414 INT_OFF;
5415 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005416 for (i = 0; i < rp->pair_count; i++) {
5417 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005418 int copy = rp->two_fd[i].copy;
5419 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005420 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005421 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005422 continue;
5423 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005424 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005425 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005426 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005427 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005428 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005429 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005430 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005431 }
5432 }
5433 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005434 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005435 free(rp);
5436 INT_ON;
5437}
5438
5439/*
5440 * Undo all redirections. Called on error or interrupt.
5441 */
5442
5443/*
5444 * Discard all saved file descriptors.
5445 */
5446static void
5447clearredir(int drop)
5448{
5449 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005450 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005451 if (!redirlist)
5452 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005453 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005454 }
5455}
5456
5457static int
5458redirectsafe(union node *redir, int flags)
5459{
5460 int err;
5461 volatile int saveint;
5462 struct jmploc *volatile savehandler = exception_handler;
5463 struct jmploc jmploc;
5464
5465 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005466 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5467 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005468 if (!err) {
5469 exception_handler = &jmploc;
5470 redirect(redir, flags);
5471 }
5472 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005473 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005474 longjmp(exception_handler->loc, 1);
5475 RESTORE_INT(saveint);
5476 return err;
5477}
5478
5479
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005480/* ============ Routines to expand arguments to commands
5481 *
5482 * We have to deal with backquotes, shell variables, and file metacharacters.
5483 */
5484
Mike Frysinger98c52642009-04-02 10:02:37 +00005485#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005486static arith_t
5487ash_arith(const char *s)
5488{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005489 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005490 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005491
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005492 math_state.lookupvar = lookupvar;
5493 math_state.setvar = setvar2;
5494 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005495
5496 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005497 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005498 if (math_state.errmsg)
5499 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005500 INT_ON;
5501
5502 return result;
5503}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005504#endif
5505
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005506/*
5507 * expandarg flags
5508 */
5509#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5510#define EXP_TILDE 0x2 /* do normal tilde expansion */
5511#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5512#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5513#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5514#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5515#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5516#define EXP_WORD 0x80 /* expand word in parameter expansion */
5517#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5518/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005519 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005520 */
5521#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5522#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5523#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5524#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5525#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5526
5527/*
5528 * Structure specifying which parts of the string should be searched
5529 * for IFS characters.
5530 */
5531struct ifsregion {
5532 struct ifsregion *next; /* next region in list */
5533 int begoff; /* offset of start of region */
5534 int endoff; /* offset of end of region */
5535 int nulonly; /* search for nul bytes only */
5536};
5537
5538struct arglist {
5539 struct strlist *list;
5540 struct strlist **lastp;
5541};
5542
5543/* output of current string */
5544static char *expdest;
5545/* list of back quote expressions */
5546static struct nodelist *argbackq;
5547/* first struct in list of ifs regions */
5548static struct ifsregion ifsfirst;
5549/* last struct in list */
5550static struct ifsregion *ifslastp;
5551/* holds expanded arg list */
5552static struct arglist exparg;
5553
5554/*
5555 * Our own itoa().
5556 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005557#if !ENABLE_SH_MATH_SUPPORT
5558/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5559typedef long arith_t;
5560# define ARITH_FMT "%ld"
5561#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005562static int
5563cvtnum(arith_t num)
5564{
5565 int len;
5566
5567 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005568 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569 STADJUST(len, expdest);
5570 return len;
5571}
5572
5573static size_t
5574esclen(const char *start, const char *p)
5575{
5576 size_t esc = 0;
5577
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005578 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005579 esc++;
5580 }
5581 return esc;
5582}
5583
5584/*
5585 * Remove any CTLESC characters from a string.
5586 */
5587static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005588rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005589{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005590 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005591
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005592 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005593 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005594 unsigned protect_against_glob;
5595 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596
5597 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005598 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005599 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005600
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005601 q = p;
5602 r = str;
5603 if (flag & RMESCAPE_ALLOC) {
5604 size_t len = p - str;
5605 size_t fulllen = len + strlen(p) + 1;
5606
5607 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005608 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005609 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005610 /* p and str may be invalidated by makestrspace */
5611 str = (char *)stackblock() + strloc;
5612 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005613 } else if (flag & RMESCAPE_HEAP) {
5614 r = ckmalloc(fulllen);
5615 } else {
5616 r = stalloc(fulllen);
5617 }
5618 q = r;
5619 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005620 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005621 }
5622 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005623
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005624 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5625 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005626 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005627 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005628 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005629// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5630// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5631// Note: both inquotes and protect_against_glob only affect whether
5632// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005633 inquotes = ~inquotes;
5634 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005635 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005636 continue;
5637 }
5638 if (*p == '\\') {
5639 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005640 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005641 goto copy;
5642 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005643 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005644 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005645 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005646 *q++ = '\\';
5647 }
5648 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005649 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005650 copy:
5651 *q++ = *p++;
5652 }
5653 *q = '\0';
5654 if (flag & RMESCAPE_GROW) {
5655 expdest = r;
5656 STADJUST(q - r + 1, expdest);
5657 }
5658 return r;
5659}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005660#define pmatch(a, b) !fnmatch((a), (b), 0)
5661
5662/*
5663 * Prepare a pattern for a expmeta (internal glob(3)) call.
5664 *
5665 * Returns an stalloced string.
5666 */
5667static char *
5668preglob(const char *pattern, int quoted, int flag)
5669{
5670 flag |= RMESCAPE_GLOB;
5671 if (quoted) {
5672 flag |= RMESCAPE_QUOTED;
5673 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005674 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005675}
5676
5677/*
5678 * Put a string on the stack.
5679 */
5680static void
5681memtodest(const char *p, size_t len, int syntax, int quotes)
5682{
5683 char *q = expdest;
5684
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005685 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005686
5687 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005688 unsigned char c = *p++;
5689 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005690 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005691 if (quotes) {
5692 int n = SIT(c, syntax);
5693 if (n == CCTL || n == CBACK)
5694 USTPUTC(CTLESC, q);
5695 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005696 USTPUTC(c, q);
5697 }
5698
5699 expdest = q;
5700}
5701
5702static void
5703strtodest(const char *p, int syntax, int quotes)
5704{
5705 memtodest(p, strlen(p), syntax, quotes);
5706}
5707
5708/*
5709 * Record the fact that we have to scan this region of the
5710 * string for IFS characters.
5711 */
5712static void
5713recordregion(int start, int end, int nulonly)
5714{
5715 struct ifsregion *ifsp;
5716
5717 if (ifslastp == NULL) {
5718 ifsp = &ifsfirst;
5719 } else {
5720 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005721 ifsp = ckzalloc(sizeof(*ifsp));
5722 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005723 ifslastp->next = ifsp;
5724 INT_ON;
5725 }
5726 ifslastp = ifsp;
5727 ifslastp->begoff = start;
5728 ifslastp->endoff = end;
5729 ifslastp->nulonly = nulonly;
5730}
5731
5732static void
5733removerecordregions(int endoff)
5734{
5735 if (ifslastp == NULL)
5736 return;
5737
5738 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005739 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005740 struct ifsregion *ifsp;
5741 INT_OFF;
5742 ifsp = ifsfirst.next->next;
5743 free(ifsfirst.next);
5744 ifsfirst.next = ifsp;
5745 INT_ON;
5746 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005747 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005748 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005749 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005750 ifslastp = &ifsfirst;
5751 ifsfirst.endoff = endoff;
5752 }
5753 return;
5754 }
5755
5756 ifslastp = &ifsfirst;
5757 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005758 ifslastp = ifslastp->next;
5759 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005760 struct ifsregion *ifsp;
5761 INT_OFF;
5762 ifsp = ifslastp->next->next;
5763 free(ifslastp->next);
5764 ifslastp->next = ifsp;
5765 INT_ON;
5766 }
5767 if (ifslastp->endoff > endoff)
5768 ifslastp->endoff = endoff;
5769}
5770
5771static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005772exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005773{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005774 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005775 char *name;
5776 struct passwd *pw;
5777 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005778 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005779 int startloc;
5780
5781 name = p + 1;
5782
5783 while ((c = *++p) != '\0') {
5784 switch (c) {
5785 case CTLESC:
5786 return startp;
5787 case CTLQUOTEMARK:
5788 return startp;
5789 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005790 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005791 goto done;
5792 break;
5793 case '/':
5794 case CTLENDVAR:
5795 goto done;
5796 }
5797 }
5798 done:
5799 *p = '\0';
5800 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005801 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005802 } else {
5803 pw = getpwnam(name);
5804 if (pw == NULL)
5805 goto lose;
5806 home = pw->pw_dir;
5807 }
5808 if (!home || !*home)
5809 goto lose;
5810 *p = c;
5811 startloc = expdest - (char *)stackblock();
5812 strtodest(home, SQSYNTAX, quotes);
5813 recordregion(startloc, expdest - (char *)stackblock(), 0);
5814 return p;
5815 lose:
5816 *p = c;
5817 return startp;
5818}
5819
5820/*
5821 * Execute a command inside back quotes. If it's a builtin command, we
5822 * want to save its output in a block obtained from malloc. Otherwise
5823 * we fork off a subprocess and get the output of the command via a pipe.
5824 * Should be called with interrupts off.
5825 */
5826struct backcmd { /* result of evalbackcmd */
5827 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005828 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005829 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005830 struct job *jp; /* job structure for command */
5831};
5832
5833/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005834static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005835#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005836static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005837
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005838static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005839evalbackcmd(union node *n, struct backcmd *result)
5840{
5841 int saveherefd;
5842
5843 result->fd = -1;
5844 result->buf = NULL;
5845 result->nleft = 0;
5846 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005847 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005848 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005849
5850 saveherefd = herefd;
5851 herefd = -1;
5852
5853 {
5854 int pip[2];
5855 struct job *jp;
5856
5857 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005858 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005859 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5861 FORCE_INT_ON;
5862 close(pip[0]);
5863 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005864 /*close(1);*/
5865 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 close(pip[1]);
5867 }
5868 eflag = 0;
5869 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5870 /* NOTREACHED */
5871 }
5872 close(pip[1]);
5873 result->fd = pip[0];
5874 result->jp = jp;
5875 }
5876 herefd = saveherefd;
5877 out:
5878 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5879 result->fd, result->buf, result->nleft, result->jp));
5880}
5881
5882/*
5883 * Expand stuff in backwards quotes.
5884 */
5885static void
5886expbackq(union node *cmd, int quoted, int quotes)
5887{
5888 struct backcmd in;
5889 int i;
5890 char buf[128];
5891 char *p;
5892 char *dest;
5893 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005894 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005895 struct stackmark smark;
5896
5897 INT_OFF;
5898 setstackmark(&smark);
5899 dest = expdest;
5900 startloc = dest - (char *)stackblock();
5901 grabstackstr(dest);
5902 evalbackcmd(cmd, &in);
5903 popstackmark(&smark);
5904
5905 p = in.buf;
5906 i = in.nleft;
5907 if (i == 0)
5908 goto read;
5909 for (;;) {
5910 memtodest(p, i, syntax, quotes);
5911 read:
5912 if (in.fd < 0)
5913 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005914 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005915 TRACE(("expbackq: read returns %d\n", i));
5916 if (i <= 0)
5917 break;
5918 p = buf;
5919 }
5920
Denis Vlasenko60818682007-09-28 22:07:23 +00005921 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005922 if (in.fd >= 0) {
5923 close(in.fd);
5924 back_exitstatus = waitforjob(in.jp);
5925 }
5926 INT_ON;
5927
5928 /* Eat all trailing newlines */
5929 dest = expdest;
5930 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5931 STUNPUTC(dest);
5932 expdest = dest;
5933
5934 if (quoted == 0)
5935 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005936 TRACE(("evalbackq: size:%d:'%.*s'\n",
5937 (int)((dest - (char *)stackblock()) - startloc),
5938 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005939 stackblock() + startloc));
5940}
5941
Mike Frysinger98c52642009-04-02 10:02:37 +00005942#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005943/*
5944 * Expand arithmetic expression. Backup to start of expression,
5945 * evaluate, place result in (backed up) result, adjust string position.
5946 */
5947static void
5948expari(int quotes)
5949{
5950 char *p, *start;
5951 int begoff;
5952 int flag;
5953 int len;
5954
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005955 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005956
5957 /*
5958 * This routine is slightly over-complicated for
5959 * efficiency. Next we scan backwards looking for the
5960 * start of arithmetic.
5961 */
5962 start = stackblock();
5963 p = expdest - 1;
5964 *p = '\0';
5965 p--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005966 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005967 int esc;
5968
Denys Vlasenkocd716832009-11-28 22:14:02 +01005969 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005970 p--;
5971#if DEBUG
5972 if (p < start) {
5973 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5974 }
5975#endif
5976 }
5977
5978 esc = esclen(start, p);
5979 if (!(esc % 2)) {
5980 break;
5981 }
5982
5983 p -= esc + 1;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005984 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005985
5986 begoff = p - start;
5987
5988 removerecordregions(begoff);
5989
5990 flag = p[1];
5991
5992 expdest = p;
5993
5994 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005995 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005997 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005998
5999 if (flag != '"')
6000 recordregion(begoff, begoff + len, 0);
6001}
6002#endif
6003
6004/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006005static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006006
6007/*
6008 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6009 * characters to allow for further processing. Otherwise treat
6010 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006011 *
6012 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6013 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6014 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006015 */
6016static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006017argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006018{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006019 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006020 '=',
6021 ':',
6022 CTLQUOTEMARK,
6023 CTLENDVAR,
6024 CTLESC,
6025 CTLVAR,
6026 CTLBACKQ,
6027 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006028#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006029 CTLENDARI,
6030#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006031 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006032 };
6033 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006034 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6035 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036 int inquotes;
6037 size_t length;
6038 int startloc;
6039
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006040 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006041 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006042 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 reject++;
6044 }
6045 inquotes = 0;
6046 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006047 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 char *q;
6049
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006050 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006051 tilde:
6052 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006053 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006054 q++;
6055 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006056 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006057 }
6058 start:
6059 startloc = expdest - (char *)stackblock();
6060 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006061 unsigned char c;
6062
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006063 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006064 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006065 if (c) {
6066 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006067 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006068 ) {
6069 /* c == '=' || c == ':' || c == CTLENDARI */
6070 length++;
6071 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006072 }
6073 if (length > 0) {
6074 int newloc;
6075 expdest = stack_nputstr(p, length, expdest);
6076 newloc = expdest - (char *)stackblock();
6077 if (breakall && !inquotes && newloc > startloc) {
6078 recordregion(startloc, newloc, 0);
6079 }
6080 startloc = newloc;
6081 }
6082 p += length + 1;
6083 length = 0;
6084
6085 switch (c) {
6086 case '\0':
6087 goto breakloop;
6088 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006089 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090 p--;
6091 continue;
6092 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006093 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006094 reject++;
6095 /* fall through */
6096 case ':':
6097 /*
6098 * sort of a hack - expand tildes in variable
6099 * assignments (after the first '=' and after ':'s).
6100 */
6101 if (*--p == '~') {
6102 goto tilde;
6103 }
6104 continue;
6105 }
6106
6107 switch (c) {
6108 case CTLENDVAR: /* ??? */
6109 goto breakloop;
6110 case CTLQUOTEMARK:
6111 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006112 if (!inquotes
6113 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006114 && ( p[4] == (char)CTLQUOTEMARK
6115 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006116 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006117 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006118 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006119 goto start;
6120 }
6121 inquotes = !inquotes;
6122 addquote:
6123 if (quotes) {
6124 p--;
6125 length++;
6126 startloc++;
6127 }
6128 break;
6129 case CTLESC:
6130 startloc++;
6131 length++;
6132 goto addquote;
6133 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006134 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006135 goto start;
6136 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006137 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006138 case CTLBACKQ|CTLQUOTE:
6139 expbackq(argbackq->n, c, quotes);
6140 argbackq = argbackq->next;
6141 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006142#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006143 case CTLENDARI:
6144 p--;
6145 expari(quotes);
6146 goto start;
6147#endif
6148 }
6149 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006150 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006151}
6152
6153static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006154scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6155 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006156{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006157 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006158 char c;
6159
6160 loc = startp;
6161 loc2 = rmesc;
6162 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006163 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006164 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006165
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166 c = *loc2;
6167 if (zero) {
6168 *loc2 = '\0';
6169 s = rmesc;
6170 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006171 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006172
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006173 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006174 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006175 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006176 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006177 loc++;
6178 loc++;
6179 loc2++;
6180 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006181 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182}
6183
6184static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006185scanright(char *startp, char *rmesc, char *rmescend,
6186 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006187{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006188#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6189 int try2optimize = match_at_start;
6190#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006191 int esc = 0;
6192 char *loc;
6193 char *loc2;
6194
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006195 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6196 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6197 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6198 * Logic:
6199 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6200 * and on each iteration they go back two/one char until they reach the beginning.
6201 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6202 */
6203 /* TODO: document in what other circumstances we are called. */
6204
6205 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006206 int match;
6207 char c = *loc2;
6208 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006209 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006210 *loc2 = '\0';
6211 s = rmesc;
6212 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006213 match = pmatch(pattern, s);
6214 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006215 *loc2 = c;
6216 if (match)
6217 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006218#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6219 if (try2optimize) {
6220 /* Maybe we can optimize this:
6221 * if pattern ends with unescaped *, we can avoid checking
6222 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6223 * it wont match truncated "raw_value_of_" strings too.
6224 */
6225 unsigned plen = strlen(pattern);
6226 /* Does it end with "*"? */
6227 if (plen != 0 && pattern[--plen] == '*') {
6228 /* "xxxx*" is not escaped */
6229 /* "xxx\*" is escaped */
6230 /* "xx\\*" is not escaped */
6231 /* "x\\\*" is escaped */
6232 int slashes = 0;
6233 while (plen != 0 && pattern[--plen] == '\\')
6234 slashes++;
6235 if (!(slashes & 1))
6236 break; /* ends with unescaped "*" */
6237 }
6238 try2optimize = 0;
6239 }
6240#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006241 loc--;
6242 if (quotes) {
6243 if (--esc < 0) {
6244 esc = esclen(startp, loc);
6245 }
6246 if (esc % 2) {
6247 esc--;
6248 loc--;
6249 }
6250 }
6251 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006252 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006253}
6254
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006255static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006256static void
6257varunset(const char *end, const char *var, const char *umsg, int varflags)
6258{
6259 const char *msg;
6260 const char *tail;
6261
6262 tail = nullstr;
6263 msg = "parameter not set";
6264 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006265 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006266 if (varflags & VSNUL)
6267 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006268 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006269 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006270 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006271 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006272 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273}
6274
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006275#if ENABLE_ASH_BASH_COMPAT
6276static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006277parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006278{
6279 char *idx, *repl = NULL;
6280 unsigned char c;
6281
Denys Vlasenko16149002010-08-06 22:06:21 +02006282 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006283 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006284 idx = arg;
6285 while (1) {
6286 c = *arg;
6287 if (!c)
6288 break;
6289 if (c == '/') {
6290 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006291 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006292 repl = idx + 1;
6293 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006294 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006295 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006296 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006297 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006298 /*
6299 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6300 * The result is a_\_z_c (not a\_\_z_c)!
6301 *
6302 * Enable debug prints in this function and you'll see:
6303 * ash: arg:'\\b/_\\_z_' varflags:d
6304 * ash: pattern:'\\b' repl:'_\_z_'
6305 * That is, \\b is interpreted as \\b, but \\_ as \_!
6306 * IOW: search pattern and replace string treat backslashes
6307 * differently! That is the reason why we check repl below:
6308 */
6309 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6310 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006311 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006312 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006313 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006314
6315 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006316}
6317#endif /* ENABLE_ASH_BASH_COMPAT */
6318
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006319static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006320subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006321 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006322{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006323 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006324 char *startp;
6325 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006327 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006328 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006329 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006330 int saveherefd = herefd;
6331 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006332 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006333 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006334
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006335 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6336 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006337
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006338 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006339 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6340 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006341 STPUTC('\0', expdest);
6342 herefd = saveherefd;
6343 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006344 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006345
6346 switch (subtype) {
6347 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006348 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006349 amount = startp - expdest;
6350 STADJUST(amount, expdest);
6351 return startp;
6352
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006353 case VSQUESTION:
6354 varunset(p, varname, startp, varflags);
6355 /* NOTREACHED */
6356
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006357#if ENABLE_ASH_BASH_COMPAT
6358 case VSSUBSTR:
6359 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006360 /* Read POS in ${var:POS:LEN} */
6361 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006362 len = str - startp - 1;
6363
6364 /* *loc != '\0', guaranteed by parser */
6365 if (quotes) {
6366 char *ptr;
6367
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006368 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006369 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006370 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006371 len--;
6372 ptr++;
6373 }
6374 }
6375 }
6376 orig_len = len;
6377
6378 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006379 /* ${var::LEN} */
6380 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006381 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006382 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006383 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006384 while (*loc && *loc != ':') {
6385 /* TODO?
6386 * bash complains on: var=qwe; echo ${var:1a:123}
6387 if (!isdigit(*loc))
6388 ash_msg_and_raise_error(msg_illnum, str);
6389 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006390 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006391 }
6392 if (*loc++ == ':') {
6393 len = number(loc);
6394 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006395 }
6396 if (pos >= orig_len) {
6397 pos = 0;
6398 len = 0;
6399 }
6400 if (len > (orig_len - pos))
6401 len = orig_len - pos;
6402
6403 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006404 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006405 str++;
6406 }
6407 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006408 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006409 *loc++ = *str++;
6410 *loc++ = *str++;
6411 }
6412 *loc = '\0';
6413 amount = loc - expdest;
6414 STADJUST(amount, expdest);
6415 return loc;
6416#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006417 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006418
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006419 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006420
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006421 /* We'll comeback here if we grow the stack while handling
6422 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6423 * stack will need rebasing, and we'll need to remove our work
6424 * areas each time
6425 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006426 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006427
6428 amount = expdest - ((char *)stackblock() + resetloc);
6429 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006430 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006431
6432 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006433 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006434 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006435 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006436 if (rmesc != startp) {
6437 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006438 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006439 }
6440 }
6441 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006442 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006444 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006445
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006446#if ENABLE_ASH_BASH_COMPAT
6447 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006448 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006449
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006450 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006451 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006452 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006453 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006454 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006455 }
6456
6457 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006458 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006459 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006460
6461 len = 0;
6462 idx = startp;
6463 end = str - 1;
6464 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006465 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006466 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006467 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006468 if (!loc) {
6469 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006470 char *restart_detect = stackblock();
6471 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006472 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006473 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006474 idx++;
6475 len++;
6476 STPUTC(*idx, expdest);
6477 }
6478 if (stackblock() != restart_detect)
6479 goto restart;
6480 idx++;
6481 len++;
6482 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006483 /* continue; - prone to quadratic behavior, smarter code: */
6484 if (idx >= end)
6485 break;
6486 if (str[0] == '*') {
6487 /* Pattern is "*foo". If "*foo" does not match "long_string",
6488 * it would never match "ong_string" etc, no point in trying.
6489 */
6490 goto skip_matching;
6491 }
6492 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006493 }
6494
6495 if (subtype == VSREPLACEALL) {
6496 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006497 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006498 idx++;
6499 idx++;
6500 rmesc++;
6501 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006502 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006503 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006504 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006505
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006506 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006507 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006508 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006509 if (quotes && *loc == '\\') {
6510 STPUTC(CTLESC, expdest);
6511 len++;
6512 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006513 STPUTC(*loc, expdest);
6514 if (stackblock() != restart_detect)
6515 goto restart;
6516 len++;
6517 }
6518
6519 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006520 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006521 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006522 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006523 STPUTC(*idx, expdest);
6524 if (stackblock() != restart_detect)
6525 goto restart;
6526 len++;
6527 idx++;
6528 }
6529 break;
6530 }
6531 }
6532
6533 /* We've put the replaced text into a buffer at workloc, now
6534 * move it to the right place and adjust the stack.
6535 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006536 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006537 startp = (char *)stackblock() + startloc;
6538 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006539 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006540 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006541 STADJUST(-amount, expdest);
6542 return startp;
6543 }
6544#endif /* ENABLE_ASH_BASH_COMPAT */
6545
6546 subtype -= VSTRIMRIGHT;
6547#if DEBUG
6548 if (subtype < 0 || subtype > 7)
6549 abort();
6550#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006551 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006552 zero = subtype >> 1;
6553 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6554 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6555
6556 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6557 if (loc) {
6558 if (zero) {
6559 memmove(startp, loc, str - loc);
6560 loc = startp + (str - loc) - 1;
6561 }
6562 *loc = '\0';
6563 amount = loc - expdest;
6564 STADJUST(amount, expdest);
6565 }
6566 return loc;
6567}
6568
6569/*
6570 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006571 * name parameter (examples):
6572 * ash -c 'echo $1' name:'1='
6573 * ash -c 'echo $qwe' name:'qwe='
6574 * ash -c 'echo $$' name:'$='
6575 * ash -c 'echo ${$}' name:'$='
6576 * ash -c 'echo ${$##q}' name:'$=q'
6577 * ash -c 'echo ${#$}' name:'$='
6578 * note: examples with bad shell syntax:
6579 * ash -c 'echo ${#$1}' name:'$=1'
6580 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006581 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006582static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006583varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006584{
Mike Frysinger98c52642009-04-02 10:02:37 +00006585 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006586 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006587 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588 int sepq = 0;
6589 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006590 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006591 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006592 int quoted = varflags & VSQUOTE;
6593 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006595 switch (*name) {
6596 case '$':
6597 num = rootpid;
6598 goto numvar;
6599 case '?':
6600 num = exitstatus;
6601 goto numvar;
6602 case '#':
6603 num = shellparam.nparam;
6604 goto numvar;
6605 case '!':
6606 num = backgndpid;
6607 if (num == 0)
6608 return -1;
6609 numvar:
6610 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006611 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006612 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006613 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006614 for (i = NOPTS - 1; i >= 0; i--) {
6615 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006616 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006617 len++;
6618 }
6619 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006620 check_1char_name:
6621#if 0
6622 /* handles cases similar to ${#$1} */
6623 if (name[2] != '\0')
6624 raise_error_syntax("bad substitution");
6625#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006626 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006627 case '@': {
6628 char **ap;
6629 int sep;
6630
6631 if (quoted && (flags & EXP_FULL)) {
6632 /* note: this is not meant as PEOF value */
6633 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006634 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006635 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006636 /* fall through */
6637 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006638 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006639 i = SIT(sep, syntax);
6640 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006641 sepq = 1;
6642 param:
6643 ap = shellparam.p;
6644 if (!ap)
6645 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006646 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006647 size_t partlen;
6648
6649 partlen = strlen(p);
6650 len += partlen;
6651
6652 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6653 memtodest(p, partlen, syntax, quotes);
6654
6655 if (*ap && sep) {
6656 char *q;
6657
6658 len++;
6659 if (subtype == VSPLUS || subtype == VSLENGTH) {
6660 continue;
6661 }
6662 q = expdest;
6663 if (sepq)
6664 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006665 /* note: may put NUL despite sep != 0
6666 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006667 STPUTC(sep, q);
6668 expdest = q;
6669 }
6670 }
6671 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006672 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006673 case '0':
6674 case '1':
6675 case '2':
6676 case '3':
6677 case '4':
6678 case '5':
6679 case '6':
6680 case '7':
6681 case '8':
6682 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006683 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006684 if (num < 0 || num > shellparam.nparam)
6685 return -1;
6686 p = num ? shellparam.p[num - 1] : arg0;
6687 goto value;
6688 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006689 /* NB: name has form "VAR=..." */
6690
6691 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6692 * which should be considered before we check variables. */
6693 if (var_str_list) {
6694 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6695 p = NULL;
6696 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006697 char *str, *eq;
6698 str = var_str_list->text;
6699 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006700 if (!eq) /* stop at first non-assignment */
6701 break;
6702 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006703 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006704 && strncmp(str, name, name_len) == 0
6705 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006706 p = eq;
6707 /* goto value; - WRONG! */
6708 /* think "A=1 A=2 B=$A" */
6709 }
6710 var_str_list = var_str_list->next;
6711 } while (var_str_list);
6712 if (p)
6713 goto value;
6714 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006715 p = lookupvar(name);
6716 value:
6717 if (!p)
6718 return -1;
6719
6720 len = strlen(p);
6721 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6722 memtodest(p, len, syntax, quotes);
6723 return len;
6724 }
6725
6726 if (subtype == VSPLUS || subtype == VSLENGTH)
6727 STADJUST(-len, expdest);
6728 return len;
6729}
6730
6731/*
6732 * Expand a variable, and return a pointer to the next character in the
6733 * input string.
6734 */
6735static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006736evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006737{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006738 char varflags;
6739 char subtype;
6740 char quoted;
6741 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006742 char *var;
6743 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006744 int startloc;
6745 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006746
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006747 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006748 subtype = varflags & VSTYPE;
6749 quoted = varflags & VSQUOTE;
6750 var = p;
6751 easy = (!quoted || (*var == '@' && shellparam.nparam));
6752 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006753 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006754
6755 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006756 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006757 if (varflags & VSNUL)
6758 varlen--;
6759
6760 if (subtype == VSPLUS) {
6761 varlen = -1 - varlen;
6762 goto vsplus;
6763 }
6764
6765 if (subtype == VSMINUS) {
6766 vsplus:
6767 if (varlen < 0) {
6768 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006769 p,
6770 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006771 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006772 );
6773 goto end;
6774 }
6775 if (easy)
6776 goto record;
6777 goto end;
6778 }
6779
6780 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6781 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006782 if (subevalvar(p, var, /* strloc: */ 0,
6783 subtype, startloc, varflags,
6784 /* quotes: */ 0,
6785 var_str_list)
6786 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006787 varflags &= ~VSNUL;
6788 /*
6789 * Remove any recorded regions beyond
6790 * start of variable
6791 */
6792 removerecordregions(startloc);
6793 goto again;
6794 }
6795 goto end;
6796 }
6797 if (easy)
6798 goto record;
6799 goto end;
6800 }
6801
6802 if (varlen < 0 && uflag)
6803 varunset(p, var, 0, 0);
6804
6805 if (subtype == VSLENGTH) {
6806 cvtnum(varlen > 0 ? varlen : 0);
6807 goto record;
6808 }
6809
6810 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006811 if (easy)
6812 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006813 goto end;
6814 }
6815
6816#if DEBUG
6817 switch (subtype) {
6818 case VSTRIMLEFT:
6819 case VSTRIMLEFTMAX:
6820 case VSTRIMRIGHT:
6821 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006822#if ENABLE_ASH_BASH_COMPAT
6823 case VSSUBSTR:
6824 case VSREPLACE:
6825 case VSREPLACEALL:
6826#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006827 break;
6828 default:
6829 abort();
6830 }
6831#endif
6832
6833 if (varlen >= 0) {
6834 /*
6835 * Terminate the string and start recording the pattern
6836 * right after it
6837 */
6838 STPUTC('\0', expdest);
6839 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006840 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006841 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006842//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006843 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006844 var_str_list)
6845 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006846 int amount = expdest - (
6847 (char *)stackblock() + patloc - 1
6848 );
6849 STADJUST(-amount, expdest);
6850 }
6851 /* Remove any recorded regions beyond start of variable */
6852 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006853 record:
6854 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006855 }
6856
6857 end:
6858 if (subtype != VSNORMAL) { /* skip to end of alternative */
6859 int nesting = 1;
6860 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006861 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006862 if (c == CTLESC)
6863 p++;
6864 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6865 if (varlen >= 0)
6866 argbackq = argbackq->next;
6867 } else if (c == CTLVAR) {
6868 if ((*p++ & VSTYPE) != VSNORMAL)
6869 nesting++;
6870 } else if (c == CTLENDVAR) {
6871 if (--nesting == 0)
6872 break;
6873 }
6874 }
6875 }
6876 return p;
6877}
6878
6879/*
6880 * Break the argument string into pieces based upon IFS and add the
6881 * strings to the argument list. The regions of the string to be
6882 * searched for IFS characters have been stored by recordregion.
6883 */
6884static void
6885ifsbreakup(char *string, struct arglist *arglist)
6886{
6887 struct ifsregion *ifsp;
6888 struct strlist *sp;
6889 char *start;
6890 char *p;
6891 char *q;
6892 const char *ifs, *realifs;
6893 int ifsspc;
6894 int nulonly;
6895
6896 start = string;
6897 if (ifslastp != NULL) {
6898 ifsspc = 0;
6899 nulonly = 0;
6900 realifs = ifsset() ? ifsval() : defifs;
6901 ifsp = &ifsfirst;
6902 do {
6903 p = string + ifsp->begoff;
6904 nulonly = ifsp->nulonly;
6905 ifs = nulonly ? nullstr : realifs;
6906 ifsspc = 0;
6907 while (p < string + ifsp->endoff) {
6908 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006909 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006910 p++;
6911 if (!strchr(ifs, *p)) {
6912 p++;
6913 continue;
6914 }
6915 if (!nulonly)
6916 ifsspc = (strchr(defifs, *p) != NULL);
6917 /* Ignore IFS whitespace at start */
6918 if (q == start && ifsspc) {
6919 p++;
6920 start = p;
6921 continue;
6922 }
6923 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006924 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006925 sp->text = start;
6926 *arglist->lastp = sp;
6927 arglist->lastp = &sp->next;
6928 p++;
6929 if (!nulonly) {
6930 for (;;) {
6931 if (p >= string + ifsp->endoff) {
6932 break;
6933 }
6934 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006935 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006936 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006937 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938 p = q;
6939 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006940 }
6941 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006942 if (ifsspc) {
6943 p++;
6944 ifsspc = 0;
6945 } else {
6946 p = q;
6947 break;
6948 }
6949 } else
6950 p++;
6951 }
6952 }
6953 start = p;
6954 } /* while */
6955 ifsp = ifsp->next;
6956 } while (ifsp != NULL);
6957 if (nulonly)
6958 goto add;
6959 }
6960
6961 if (!*start)
6962 return;
6963
6964 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006965 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006966 sp->text = start;
6967 *arglist->lastp = sp;
6968 arglist->lastp = &sp->next;
6969}
6970
6971static void
6972ifsfree(void)
6973{
6974 struct ifsregion *p;
6975
6976 INT_OFF;
6977 p = ifsfirst.next;
6978 do {
6979 struct ifsregion *ifsp;
6980 ifsp = p->next;
6981 free(p);
6982 p = ifsp;
6983 } while (p);
6984 ifslastp = NULL;
6985 ifsfirst.next = NULL;
6986 INT_ON;
6987}
6988
6989/*
6990 * Add a file name to the list.
6991 */
6992static void
6993addfname(const char *name)
6994{
6995 struct strlist *sp;
6996
Denis Vlasenko597906c2008-02-20 16:38:54 +00006997 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006998 sp->text = ststrdup(name);
6999 *exparg.lastp = sp;
7000 exparg.lastp = &sp->next;
7001}
7002
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007003/*
7004 * Do metacharacter (i.e. *, ?, [...]) expansion.
7005 */
7006static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007007expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007008{
7009 char *p;
7010 const char *cp;
7011 char *start;
7012 char *endname;
7013 int metaflag;
7014 struct stat statb;
7015 DIR *dirp;
7016 struct dirent *dp;
7017 int atend;
7018 int matchdot;
7019
7020 metaflag = 0;
7021 start = name;
7022 for (p = name; *p; p++) {
7023 if (*p == '*' || *p == '?')
7024 metaflag = 1;
7025 else if (*p == '[') {
7026 char *q = p + 1;
7027 if (*q == '!')
7028 q++;
7029 for (;;) {
7030 if (*q == '\\')
7031 q++;
7032 if (*q == '/' || *q == '\0')
7033 break;
7034 if (*++q == ']') {
7035 metaflag = 1;
7036 break;
7037 }
7038 }
7039 } else if (*p == '\\')
7040 p++;
7041 else if (*p == '/') {
7042 if (metaflag)
7043 goto out;
7044 start = p + 1;
7045 }
7046 }
7047 out:
7048 if (metaflag == 0) { /* we've reached the end of the file name */
7049 if (enddir != expdir)
7050 metaflag++;
7051 p = name;
7052 do {
7053 if (*p == '\\')
7054 p++;
7055 *enddir++ = *p;
7056 } while (*p++);
7057 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7058 addfname(expdir);
7059 return;
7060 }
7061 endname = p;
7062 if (name < start) {
7063 p = name;
7064 do {
7065 if (*p == '\\')
7066 p++;
7067 *enddir++ = *p++;
7068 } while (p < start);
7069 }
7070 if (enddir == expdir) {
7071 cp = ".";
7072 } else if (enddir == expdir + 1 && *expdir == '/') {
7073 cp = "/";
7074 } else {
7075 cp = expdir;
7076 enddir[-1] = '\0';
7077 }
7078 dirp = opendir(cp);
7079 if (dirp == NULL)
7080 return;
7081 if (enddir != expdir)
7082 enddir[-1] = '/';
7083 if (*endname == 0) {
7084 atend = 1;
7085 } else {
7086 atend = 0;
7087 *endname++ = '\0';
7088 }
7089 matchdot = 0;
7090 p = start;
7091 if (*p == '\\')
7092 p++;
7093 if (*p == '.')
7094 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007095 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007096 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007097 continue;
7098 if (pmatch(start, dp->d_name)) {
7099 if (atend) {
7100 strcpy(enddir, dp->d_name);
7101 addfname(expdir);
7102 } else {
7103 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7104 continue;
7105 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007106 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007107 }
7108 }
7109 }
7110 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007111 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007112 endname[-1] = '/';
7113}
7114
7115static struct strlist *
7116msort(struct strlist *list, int len)
7117{
7118 struct strlist *p, *q = NULL;
7119 struct strlist **lpp;
7120 int half;
7121 int n;
7122
7123 if (len <= 1)
7124 return list;
7125 half = len >> 1;
7126 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007127 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007128 q = p;
7129 p = p->next;
7130 }
7131 q->next = NULL; /* terminate first half of list */
7132 q = msort(list, half); /* sort first half of list */
7133 p = msort(p, len - half); /* sort second half */
7134 lpp = &list;
7135 for (;;) {
7136#if ENABLE_LOCALE_SUPPORT
7137 if (strcoll(p->text, q->text) < 0)
7138#else
7139 if (strcmp(p->text, q->text) < 0)
7140#endif
7141 {
7142 *lpp = p;
7143 lpp = &p->next;
7144 p = *lpp;
7145 if (p == NULL) {
7146 *lpp = q;
7147 break;
7148 }
7149 } else {
7150 *lpp = q;
7151 lpp = &q->next;
7152 q = *lpp;
7153 if (q == NULL) {
7154 *lpp = p;
7155 break;
7156 }
7157 }
7158 }
7159 return list;
7160}
7161
7162/*
7163 * Sort the results of file name expansion. It calculates the number of
7164 * strings to sort and then calls msort (short for merge sort) to do the
7165 * work.
7166 */
7167static struct strlist *
7168expsort(struct strlist *str)
7169{
7170 int len;
7171 struct strlist *sp;
7172
7173 len = 0;
7174 for (sp = str; sp; sp = sp->next)
7175 len++;
7176 return msort(str, len);
7177}
7178
7179static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007180expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007181{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007182 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007183 '*', '?', '[', 0
7184 };
7185 /* TODO - EXP_REDIR */
7186
7187 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007188 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007189 struct strlist **savelastp;
7190 struct strlist *sp;
7191 char *p;
7192
7193 if (fflag)
7194 goto nometa;
7195 if (!strpbrk(str->text, metachars))
7196 goto nometa;
7197 savelastp = exparg.lastp;
7198
7199 INT_OFF;
7200 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7201 {
7202 int i = strlen(str->text);
7203 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7204 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007205 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007206 free(expdir);
7207 if (p != str->text)
7208 free(p);
7209 INT_ON;
7210 if (exparg.lastp == savelastp) {
7211 /*
7212 * no matches
7213 */
7214 nometa:
7215 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007216 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007217 exparg.lastp = &str->next;
7218 } else {
7219 *exparg.lastp = NULL;
7220 *savelastp = sp = expsort(*savelastp);
7221 while (sp->next != NULL)
7222 sp = sp->next;
7223 exparg.lastp = &sp->next;
7224 }
7225 str = str->next;
7226 }
7227}
7228
7229/*
7230 * Perform variable substitution and command substitution on an argument,
7231 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7232 * perform splitting and file name expansion. When arglist is NULL, perform
7233 * here document expansion.
7234 */
7235static void
7236expandarg(union node *arg, struct arglist *arglist, int flag)
7237{
7238 struct strlist *sp;
7239 char *p;
7240
7241 argbackq = arg->narg.backquote;
7242 STARTSTACKSTR(expdest);
7243 ifsfirst.next = NULL;
7244 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007245 argstr(arg->narg.text, flag,
7246 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007247 p = _STPUTC('\0', expdest);
7248 expdest = p - 1;
7249 if (arglist == NULL) {
7250 return; /* here document expanded */
7251 }
7252 p = grabstackstr(p);
7253 exparg.lastp = &exparg.list;
7254 /*
7255 * TODO - EXP_REDIR
7256 */
7257 if (flag & EXP_FULL) {
7258 ifsbreakup(p, &exparg);
7259 *exparg.lastp = NULL;
7260 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007261 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007262 } else {
7263 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007264 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007265 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007266 sp->text = p;
7267 *exparg.lastp = sp;
7268 exparg.lastp = &sp->next;
7269 }
7270 if (ifsfirst.next)
7271 ifsfree();
7272 *exparg.lastp = NULL;
7273 if (exparg.list) {
7274 *arglist->lastp = exparg.list;
7275 arglist->lastp = exparg.lastp;
7276 }
7277}
7278
7279/*
7280 * Expand shell variables and backquotes inside a here document.
7281 */
7282static void
7283expandhere(union node *arg, int fd)
7284{
7285 herefd = fd;
7286 expandarg(arg, (struct arglist *)NULL, 0);
7287 full_write(fd, stackblock(), expdest - (char *)stackblock());
7288}
7289
7290/*
7291 * Returns true if the pattern matches the string.
7292 */
7293static int
7294patmatch(char *pattern, const char *string)
7295{
7296 return pmatch(preglob(pattern, 0, 0), string);
7297}
7298
7299/*
7300 * See if a pattern matches in a case statement.
7301 */
7302static int
7303casematch(union node *pattern, char *val)
7304{
7305 struct stackmark smark;
7306 int result;
7307
7308 setstackmark(&smark);
7309 argbackq = pattern->narg.backquote;
7310 STARTSTACKSTR(expdest);
7311 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007312 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7313 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007314 STACKSTRNUL(expdest);
7315 result = patmatch(stackblock(), val);
7316 popstackmark(&smark);
7317 return result;
7318}
7319
7320
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007321/* ============ find_command */
7322
7323struct builtincmd {
7324 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007325 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007326 /* unsigned flags; */
7327};
7328#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007329/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007330 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007331#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007332#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333
7334struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007335 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007336 union param {
7337 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007338 /* index >= 0 for commands without path (slashes) */
7339 /* (TODO: what exactly does the value mean? PATH position?) */
7340 /* index == -1 for commands with slashes */
7341 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007342 const struct builtincmd *cmd;
7343 struct funcnode *func;
7344 } u;
7345};
7346/* values of cmdtype */
7347#define CMDUNKNOWN -1 /* no entry in table for command */
7348#define CMDNORMAL 0 /* command is an executable program */
7349#define CMDFUNCTION 1 /* command is a shell function */
7350#define CMDBUILTIN 2 /* command is a shell builtin */
7351
7352/* action to find_command() */
7353#define DO_ERR 0x01 /* prints errors */
7354#define DO_ABS 0x02 /* checks absolute paths */
7355#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7356#define DO_ALTPATH 0x08 /* using alternate path */
7357#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7358
7359static void find_command(char *, struct cmdentry *, int, const char *);
7360
7361
7362/* ============ Hashing commands */
7363
7364/*
7365 * When commands are first encountered, they are entered in a hash table.
7366 * This ensures that a full path search will not have to be done for them
7367 * on each invocation.
7368 *
7369 * We should investigate converting to a linear search, even though that
7370 * would make the command name "hash" a misnomer.
7371 */
7372
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007373struct tblentry {
7374 struct tblentry *next; /* next entry in hash chain */
7375 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007376 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007377 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007378 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007379};
7380
Denis Vlasenko01631112007-12-16 17:20:38 +00007381static struct tblentry **cmdtable;
7382#define INIT_G_cmdtable() do { \
7383 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7384} while (0)
7385
7386static int builtinloc = -1; /* index in path of %builtin, or -1 */
7387
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007388
7389static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007390tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007392#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007393 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007394 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007395 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007396 while (*envp)
7397 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007398 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007399 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007400 /* re-exec ourselves with the new arguments */
7401 execve(bb_busybox_exec_path, argv, envp);
7402 /* If they called chroot or otherwise made the binary no longer
7403 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007404 }
7405#endif
7406
7407 repeat:
7408#ifdef SYSV
7409 do {
7410 execve(cmd, argv, envp);
7411 } while (errno == EINTR);
7412#else
7413 execve(cmd, argv, envp);
7414#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007415 if (cmd == (char*) bb_busybox_exec_path) {
7416 /* We already visited ENOEXEC branch below, don't do it again */
7417//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007418 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007419 return;
7420 }
7421 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007422 /* Run "cmd" as a shell script:
7423 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7424 * "If the execve() function fails with ENOEXEC, the shell
7425 * shall execute a command equivalent to having a shell invoked
7426 * with the command name as its first operand,
7427 * with any remaining arguments passed to the new shell"
7428 *
7429 * That is, do not use $SHELL, user's shell, or /bin/sh;
7430 * just call ourselves.
7431 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007432 char **ap;
7433 char **new;
7434
7435 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007436 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007437 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7438 new[0] = (char*) "ash";
7439 new[1] = cmd;
7440 ap = new + 2;
7441 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007442 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007443 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007444 argv = new;
7445 goto repeat;
7446 }
7447}
7448
7449/*
7450 * Exec a program. Never returns. If you change this routine, you may
7451 * have to change the find_command routine as well.
7452 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007453static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007454static void
7455shellexec(char **argv, const char *path, int idx)
7456{
7457 char *cmdname;
7458 int e;
7459 char **envp;
7460 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007461#if ENABLE_FEATURE_SH_STANDALONE
7462 int applet_no = -1;
7463#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007464
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007465 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007466 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007467 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007468#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007469 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007470#endif
7471 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007472 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007473 e = errno;
7474 } else {
7475 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007476 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007477 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007478 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007479 if (errno != ENOENT && errno != ENOTDIR)
7480 e = errno;
7481 }
7482 stunalloc(cmdname);
7483 }
7484 }
7485
7486 /* Map to POSIX errors */
7487 switch (e) {
7488 case EACCES:
7489 exerrno = 126;
7490 break;
7491 case ENOENT:
7492 exerrno = 127;
7493 break;
7494 default:
7495 exerrno = 2;
7496 break;
7497 }
7498 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007499 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7500 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007501 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7502 /* NOTREACHED */
7503}
7504
7505static void
7506printentry(struct tblentry *cmdp)
7507{
7508 int idx;
7509 const char *path;
7510 char *name;
7511
7512 idx = cmdp->param.index;
7513 path = pathval();
7514 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007515 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007516 stunalloc(name);
7517 } while (--idx >= 0);
7518 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7519}
7520
7521/*
7522 * Clear out command entries. The argument specifies the first entry in
7523 * PATH which has changed.
7524 */
7525static void
7526clearcmdentry(int firstchange)
7527{
7528 struct tblentry **tblp;
7529 struct tblentry **pp;
7530 struct tblentry *cmdp;
7531
7532 INT_OFF;
7533 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7534 pp = tblp;
7535 while ((cmdp = *pp) != NULL) {
7536 if ((cmdp->cmdtype == CMDNORMAL &&
7537 cmdp->param.index >= firstchange)
7538 || (cmdp->cmdtype == CMDBUILTIN &&
7539 builtinloc >= firstchange)
7540 ) {
7541 *pp = cmdp->next;
7542 free(cmdp);
7543 } else {
7544 pp = &cmdp->next;
7545 }
7546 }
7547 }
7548 INT_ON;
7549}
7550
7551/*
7552 * Locate a command in the command hash table. If "add" is nonzero,
7553 * add the command to the table if it is not already present. The
7554 * variable "lastcmdentry" is set to point to the address of the link
7555 * pointing to the entry, so that delete_cmd_entry can delete the
7556 * entry.
7557 *
7558 * Interrupts must be off if called with add != 0.
7559 */
7560static struct tblentry **lastcmdentry;
7561
7562static struct tblentry *
7563cmdlookup(const char *name, int add)
7564{
7565 unsigned int hashval;
7566 const char *p;
7567 struct tblentry *cmdp;
7568 struct tblentry **pp;
7569
7570 p = name;
7571 hashval = (unsigned char)*p << 4;
7572 while (*p)
7573 hashval += (unsigned char)*p++;
7574 hashval &= 0x7FFF;
7575 pp = &cmdtable[hashval % CMDTABLESIZE];
7576 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7577 if (strcmp(cmdp->cmdname, name) == 0)
7578 break;
7579 pp = &cmdp->next;
7580 }
7581 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007582 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7583 + strlen(name)
7584 /* + 1 - already done because
7585 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007586 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007587 cmdp->cmdtype = CMDUNKNOWN;
7588 strcpy(cmdp->cmdname, name);
7589 }
7590 lastcmdentry = pp;
7591 return cmdp;
7592}
7593
7594/*
7595 * Delete the command entry returned on the last lookup.
7596 */
7597static void
7598delete_cmd_entry(void)
7599{
7600 struct tblentry *cmdp;
7601
7602 INT_OFF;
7603 cmdp = *lastcmdentry;
7604 *lastcmdentry = cmdp->next;
7605 if (cmdp->cmdtype == CMDFUNCTION)
7606 freefunc(cmdp->param.func);
7607 free(cmdp);
7608 INT_ON;
7609}
7610
7611/*
7612 * Add a new command entry, replacing any existing command entry for
7613 * the same name - except special builtins.
7614 */
7615static void
7616addcmdentry(char *name, struct cmdentry *entry)
7617{
7618 struct tblentry *cmdp;
7619
7620 cmdp = cmdlookup(name, 1);
7621 if (cmdp->cmdtype == CMDFUNCTION) {
7622 freefunc(cmdp->param.func);
7623 }
7624 cmdp->cmdtype = entry->cmdtype;
7625 cmdp->param = entry->u;
7626 cmdp->rehash = 0;
7627}
7628
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007629static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007630hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007631{
7632 struct tblentry **pp;
7633 struct tblentry *cmdp;
7634 int c;
7635 struct cmdentry entry;
7636 char *name;
7637
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007638 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007639 clearcmdentry(0);
7640 return 0;
7641 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007642
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007643 if (*argptr == NULL) {
7644 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7645 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7646 if (cmdp->cmdtype == CMDNORMAL)
7647 printentry(cmdp);
7648 }
7649 }
7650 return 0;
7651 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007652
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007653 c = 0;
7654 while ((name = *argptr) != NULL) {
7655 cmdp = cmdlookup(name, 0);
7656 if (cmdp != NULL
7657 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007658 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7659 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007660 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007661 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662 find_command(name, &entry, DO_ERR, pathval());
7663 if (entry.cmdtype == CMDUNKNOWN)
7664 c = 1;
7665 argptr++;
7666 }
7667 return c;
7668}
7669
7670/*
7671 * Called when a cd is done. Marks all commands so the next time they
7672 * are executed they will be rehashed.
7673 */
7674static void
7675hashcd(void)
7676{
7677 struct tblentry **pp;
7678 struct tblentry *cmdp;
7679
7680 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7681 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007682 if (cmdp->cmdtype == CMDNORMAL
7683 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007684 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007685 && builtinloc > 0)
7686 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007687 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007688 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007689 }
7690 }
7691}
7692
7693/*
7694 * Fix command hash table when PATH changed.
7695 * Called before PATH is changed. The argument is the new value of PATH;
7696 * pathval() still returns the old value at this point.
7697 * Called with interrupts off.
7698 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007699static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007700changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007701{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007702 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007703 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007704 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007705 int idx_bltin;
7706
7707 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007708 firstchange = 9999; /* assume no change */
7709 idx = 0;
7710 idx_bltin = -1;
7711 for (;;) {
7712 if (*old != *new) {
7713 firstchange = idx;
7714 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007715 || (*old == ':' && *new == '\0')
7716 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007717 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007718 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007719 old = new; /* ignore subsequent differences */
7720 }
7721 if (*new == '\0')
7722 break;
7723 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7724 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007725 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007726 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007727 new++;
7728 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007729 }
7730 if (builtinloc < 0 && idx_bltin >= 0)
7731 builtinloc = idx_bltin; /* zap builtins */
7732 if (builtinloc >= 0 && idx_bltin < 0)
7733 firstchange = 0;
7734 clearcmdentry(firstchange);
7735 builtinloc = idx_bltin;
7736}
7737
7738#define TEOF 0
7739#define TNL 1
7740#define TREDIR 2
7741#define TWORD 3
7742#define TSEMI 4
7743#define TBACKGND 5
7744#define TAND 6
7745#define TOR 7
7746#define TPIPE 8
7747#define TLP 9
7748#define TRP 10
7749#define TENDCASE 11
7750#define TENDBQUOTE 12
7751#define TNOT 13
7752#define TCASE 14
7753#define TDO 15
7754#define TDONE 16
7755#define TELIF 17
7756#define TELSE 18
7757#define TESAC 19
7758#define TFI 20
7759#define TFOR 21
7760#define TIF 22
7761#define TIN 23
7762#define TTHEN 24
7763#define TUNTIL 25
7764#define TWHILE 26
7765#define TBEGIN 27
7766#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007767typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007768
7769/* first char is indicating which tokens mark the end of a list */
7770static const char *const tokname_array[] = {
7771 "\1end of file",
7772 "\0newline",
7773 "\0redirection",
7774 "\0word",
7775 "\0;",
7776 "\0&",
7777 "\0&&",
7778 "\0||",
7779 "\0|",
7780 "\0(",
7781 "\1)",
7782 "\1;;",
7783 "\1`",
7784#define KWDOFFSET 13
7785 /* the following are keywords */
7786 "\0!",
7787 "\0case",
7788 "\1do",
7789 "\1done",
7790 "\1elif",
7791 "\1else",
7792 "\1esac",
7793 "\1fi",
7794 "\0for",
7795 "\0if",
7796 "\0in",
7797 "\1then",
7798 "\0until",
7799 "\0while",
7800 "\0{",
7801 "\1}",
7802};
7803
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007804/* Wrapper around strcmp for qsort/bsearch/... */
7805static int
7806pstrcmp(const void *a, const void *b)
7807{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007808 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007809}
7810
7811static const char *const *
7812findkwd(const char *s)
7813{
7814 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007815 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7816 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007817}
7818
7819/*
7820 * Locate and print what a word is...
7821 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007822static int
7823describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007824{
7825 struct cmdentry entry;
7826 struct tblentry *cmdp;
7827#if ENABLE_ASH_ALIAS
7828 const struct alias *ap;
7829#endif
7830 const char *path = pathval();
7831
7832 if (describe_command_verbose) {
7833 out1str(command);
7834 }
7835
7836 /* First look at the keywords */
7837 if (findkwd(command)) {
7838 out1str(describe_command_verbose ? " is a shell keyword" : command);
7839 goto out;
7840 }
7841
7842#if ENABLE_ASH_ALIAS
7843 /* Then look at the aliases */
7844 ap = lookupalias(command, 0);
7845 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007846 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007847 out1str("alias ");
7848 printalias(ap);
7849 return 0;
7850 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007851 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007852 goto out;
7853 }
7854#endif
7855 /* Then check if it is a tracked alias */
7856 cmdp = cmdlookup(command, 0);
7857 if (cmdp != NULL) {
7858 entry.cmdtype = cmdp->cmdtype;
7859 entry.u = cmdp->param;
7860 } else {
7861 /* Finally use brute force */
7862 find_command(command, &entry, DO_ABS, path);
7863 }
7864
7865 switch (entry.cmdtype) {
7866 case CMDNORMAL: {
7867 int j = entry.u.index;
7868 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007869 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007870 p = command;
7871 } else {
7872 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007873 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007874 stunalloc(p);
7875 } while (--j >= 0);
7876 }
7877 if (describe_command_verbose) {
7878 out1fmt(" is%s %s",
7879 (cmdp ? " a tracked alias for" : nullstr), p
7880 );
7881 } else {
7882 out1str(p);
7883 }
7884 break;
7885 }
7886
7887 case CMDFUNCTION:
7888 if (describe_command_verbose) {
7889 out1str(" is a shell function");
7890 } else {
7891 out1str(command);
7892 }
7893 break;
7894
7895 case CMDBUILTIN:
7896 if (describe_command_verbose) {
7897 out1fmt(" is a %sshell builtin",
7898 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7899 "special " : nullstr
7900 );
7901 } else {
7902 out1str(command);
7903 }
7904 break;
7905
7906 default:
7907 if (describe_command_verbose) {
7908 out1str(": not found\n");
7909 }
7910 return 127;
7911 }
7912 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007913 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007914 return 0;
7915}
7916
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007917static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007918typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007919{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007920 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007921 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007922 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007923
Denis Vlasenko46846e22007-05-20 13:08:31 +00007924 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007925 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007926 i++;
7927 verbose = 0;
7928 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007929 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007930 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007931 }
7932 return err;
7933}
7934
7935#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007936static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007937commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007938{
7939 int c;
7940 enum {
7941 VERIFY_BRIEF = 1,
7942 VERIFY_VERBOSE = 2,
7943 } verify = 0;
7944
7945 while ((c = nextopt("pvV")) != '\0')
7946 if (c == 'V')
7947 verify |= VERIFY_VERBOSE;
7948 else if (c == 'v')
7949 verify |= VERIFY_BRIEF;
7950#if DEBUG
7951 else if (c != 'p')
7952 abort();
7953#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007954 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7955 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007956 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007957 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007958
7959 return 0;
7960}
7961#endif
7962
7963
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007964/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007965
Denis Vlasenko340299a2008-11-21 10:36:36 +00007966static int funcblocksize; /* size of structures in function */
7967static int funcstringsize; /* size of strings in node */
7968static void *funcblock; /* block to allocate function from */
7969static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007970
Eric Andersencb57d552001-06-28 07:25:16 +00007971/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007972#define EV_EXIT 01 /* exit after evaluating tree */
7973#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007974#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007975
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007976static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007977 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7978 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7979 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7980 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7981 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7982 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7983 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7984 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7985 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7986 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7987 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7988 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7989 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7990 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7991 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7992 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7993 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007994#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007995 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007996#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007997 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7998 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7999 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8000 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8001 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8002 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8003 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8004 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8005 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008006};
8007
8008static void calcsize(union node *n);
8009
8010static void
8011sizenodelist(struct nodelist *lp)
8012{
8013 while (lp) {
8014 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8015 calcsize(lp->n);
8016 lp = lp->next;
8017 }
8018}
8019
8020static void
8021calcsize(union node *n)
8022{
8023 if (n == NULL)
8024 return;
8025 funcblocksize += nodesize[n->type];
8026 switch (n->type) {
8027 case NCMD:
8028 calcsize(n->ncmd.redirect);
8029 calcsize(n->ncmd.args);
8030 calcsize(n->ncmd.assign);
8031 break;
8032 case NPIPE:
8033 sizenodelist(n->npipe.cmdlist);
8034 break;
8035 case NREDIR:
8036 case NBACKGND:
8037 case NSUBSHELL:
8038 calcsize(n->nredir.redirect);
8039 calcsize(n->nredir.n);
8040 break;
8041 case NAND:
8042 case NOR:
8043 case NSEMI:
8044 case NWHILE:
8045 case NUNTIL:
8046 calcsize(n->nbinary.ch2);
8047 calcsize(n->nbinary.ch1);
8048 break;
8049 case NIF:
8050 calcsize(n->nif.elsepart);
8051 calcsize(n->nif.ifpart);
8052 calcsize(n->nif.test);
8053 break;
8054 case NFOR:
8055 funcstringsize += strlen(n->nfor.var) + 1;
8056 calcsize(n->nfor.body);
8057 calcsize(n->nfor.args);
8058 break;
8059 case NCASE:
8060 calcsize(n->ncase.cases);
8061 calcsize(n->ncase.expr);
8062 break;
8063 case NCLIST:
8064 calcsize(n->nclist.body);
8065 calcsize(n->nclist.pattern);
8066 calcsize(n->nclist.next);
8067 break;
8068 case NDEFUN:
8069 case NARG:
8070 sizenodelist(n->narg.backquote);
8071 funcstringsize += strlen(n->narg.text) + 1;
8072 calcsize(n->narg.next);
8073 break;
8074 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008075#if ENABLE_ASH_BASH_COMPAT
8076 case NTO2:
8077#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008078 case NCLOBBER:
8079 case NFROM:
8080 case NFROMTO:
8081 case NAPPEND:
8082 calcsize(n->nfile.fname);
8083 calcsize(n->nfile.next);
8084 break;
8085 case NTOFD:
8086 case NFROMFD:
8087 calcsize(n->ndup.vname);
8088 calcsize(n->ndup.next);
8089 break;
8090 case NHERE:
8091 case NXHERE:
8092 calcsize(n->nhere.doc);
8093 calcsize(n->nhere.next);
8094 break;
8095 case NNOT:
8096 calcsize(n->nnot.com);
8097 break;
8098 };
8099}
8100
8101static char *
8102nodeckstrdup(char *s)
8103{
8104 char *rtn = funcstring;
8105
8106 strcpy(funcstring, s);
8107 funcstring += strlen(s) + 1;
8108 return rtn;
8109}
8110
8111static union node *copynode(union node *);
8112
8113static struct nodelist *
8114copynodelist(struct nodelist *lp)
8115{
8116 struct nodelist *start;
8117 struct nodelist **lpp;
8118
8119 lpp = &start;
8120 while (lp) {
8121 *lpp = funcblock;
8122 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8123 (*lpp)->n = copynode(lp->n);
8124 lp = lp->next;
8125 lpp = &(*lpp)->next;
8126 }
8127 *lpp = NULL;
8128 return start;
8129}
8130
8131static union node *
8132copynode(union node *n)
8133{
8134 union node *new;
8135
8136 if (n == NULL)
8137 return NULL;
8138 new = funcblock;
8139 funcblock = (char *) funcblock + nodesize[n->type];
8140
8141 switch (n->type) {
8142 case NCMD:
8143 new->ncmd.redirect = copynode(n->ncmd.redirect);
8144 new->ncmd.args = copynode(n->ncmd.args);
8145 new->ncmd.assign = copynode(n->ncmd.assign);
8146 break;
8147 case NPIPE:
8148 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008149 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008150 break;
8151 case NREDIR:
8152 case NBACKGND:
8153 case NSUBSHELL:
8154 new->nredir.redirect = copynode(n->nredir.redirect);
8155 new->nredir.n = copynode(n->nredir.n);
8156 break;
8157 case NAND:
8158 case NOR:
8159 case NSEMI:
8160 case NWHILE:
8161 case NUNTIL:
8162 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8163 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8164 break;
8165 case NIF:
8166 new->nif.elsepart = copynode(n->nif.elsepart);
8167 new->nif.ifpart = copynode(n->nif.ifpart);
8168 new->nif.test = copynode(n->nif.test);
8169 break;
8170 case NFOR:
8171 new->nfor.var = nodeckstrdup(n->nfor.var);
8172 new->nfor.body = copynode(n->nfor.body);
8173 new->nfor.args = copynode(n->nfor.args);
8174 break;
8175 case NCASE:
8176 new->ncase.cases = copynode(n->ncase.cases);
8177 new->ncase.expr = copynode(n->ncase.expr);
8178 break;
8179 case NCLIST:
8180 new->nclist.body = copynode(n->nclist.body);
8181 new->nclist.pattern = copynode(n->nclist.pattern);
8182 new->nclist.next = copynode(n->nclist.next);
8183 break;
8184 case NDEFUN:
8185 case NARG:
8186 new->narg.backquote = copynodelist(n->narg.backquote);
8187 new->narg.text = nodeckstrdup(n->narg.text);
8188 new->narg.next = copynode(n->narg.next);
8189 break;
8190 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008191#if ENABLE_ASH_BASH_COMPAT
8192 case NTO2:
8193#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008194 case NCLOBBER:
8195 case NFROM:
8196 case NFROMTO:
8197 case NAPPEND:
8198 new->nfile.fname = copynode(n->nfile.fname);
8199 new->nfile.fd = n->nfile.fd;
8200 new->nfile.next = copynode(n->nfile.next);
8201 break;
8202 case NTOFD:
8203 case NFROMFD:
8204 new->ndup.vname = copynode(n->ndup.vname);
8205 new->ndup.dupfd = n->ndup.dupfd;
8206 new->ndup.fd = n->ndup.fd;
8207 new->ndup.next = copynode(n->ndup.next);
8208 break;
8209 case NHERE:
8210 case NXHERE:
8211 new->nhere.doc = copynode(n->nhere.doc);
8212 new->nhere.fd = n->nhere.fd;
8213 new->nhere.next = copynode(n->nhere.next);
8214 break;
8215 case NNOT:
8216 new->nnot.com = copynode(n->nnot.com);
8217 break;
8218 };
8219 new->type = n->type;
8220 return new;
8221}
8222
8223/*
8224 * Make a copy of a parse tree.
8225 */
8226static struct funcnode *
8227copyfunc(union node *n)
8228{
8229 struct funcnode *f;
8230 size_t blocksize;
8231
8232 funcblocksize = offsetof(struct funcnode, n);
8233 funcstringsize = 0;
8234 calcsize(n);
8235 blocksize = funcblocksize;
8236 f = ckmalloc(blocksize + funcstringsize);
8237 funcblock = (char *) f + offsetof(struct funcnode, n);
8238 funcstring = (char *) f + blocksize;
8239 copynode(n);
8240 f->count = 0;
8241 return f;
8242}
8243
8244/*
8245 * Define a shell function.
8246 */
8247static void
8248defun(char *name, union node *func)
8249{
8250 struct cmdentry entry;
8251
8252 INT_OFF;
8253 entry.cmdtype = CMDFUNCTION;
8254 entry.u.func = copyfunc(func);
8255 addcmdentry(name, &entry);
8256 INT_ON;
8257}
8258
Denis Vlasenko4b875702009-03-19 13:30:04 +00008259/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008260#define SKIPBREAK (1 << 0)
8261#define SKIPCONT (1 << 1)
8262#define SKIPFUNC (1 << 2)
8263#define SKIPFILE (1 << 3)
8264#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008265static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008266static int skipcount; /* number of levels to skip */
8267static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008268static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008269
Denis Vlasenko4b875702009-03-19 13:30:04 +00008270/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008271static int evalstring(char *s, int mask);
8272
Denis Vlasenko4b875702009-03-19 13:30:04 +00008273/* Called to execute a trap.
8274 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008275 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008276 *
8277 * Perhaps we should avoid entering new trap handlers
8278 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008279 */
8280static int
8281dotrap(void)
8282{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008283 uint8_t *g;
8284 int sig;
8285 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008286
8287 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008288 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008289 xbarrier();
8290
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008291 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008292 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8293 int want_exexit;
8294 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008295
Denis Vlasenko4b875702009-03-19 13:30:04 +00008296 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008297 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008298 t = trap[sig];
8299 /* non-trapped SIGINT is handled separately by raise_interrupt,
8300 * don't upset it by resetting gotsig[SIGINT-1] */
8301 if (sig == SIGINT && !t)
8302 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008303
8304 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008305 *g = 0;
8306 if (!t)
8307 continue;
8308 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008309 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008310 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008311 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008312 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008313 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008314 }
8315
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008316 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008317 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008318}
8319
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008320/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008321static void evalloop(union node *, int);
8322static void evalfor(union node *, int);
8323static void evalcase(union node *, int);
8324static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008325static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008326static void evalpipe(union node *, int);
8327static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008328static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008329static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008330
Eric Andersen62483552001-07-10 06:09:16 +00008331/*
Eric Andersenc470f442003-07-28 09:56:35 +00008332 * Evaluate a parse tree. The value is left in the global variable
8333 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008334 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008335static void
Eric Andersenc470f442003-07-28 09:56:35 +00008336evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008337{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008338 struct jmploc *volatile savehandler = exception_handler;
8339 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008340 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008341 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008342 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008343 int int_level;
8344
8345 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008346
Eric Andersenc470f442003-07-28 09:56:35 +00008347 if (n == NULL) {
8348 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008349 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008350 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008351 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008352
8353 exception_handler = &jmploc;
8354 {
8355 int err = setjmp(jmploc.loc);
8356 if (err) {
8357 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008358 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008359 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8360 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008361 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008362 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008363 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008364 TRACE(("exception %d in evaltree, propagating err=%d\n",
8365 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008366 exception_handler = savehandler;
8367 longjmp(exception_handler->loc, err);
8368 }
8369 }
8370
Eric Andersenc470f442003-07-28 09:56:35 +00008371 switch (n->type) {
8372 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008373#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008374 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008375 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008376 break;
8377#endif
8378 case NNOT:
8379 evaltree(n->nnot.com, EV_TESTED);
8380 status = !exitstatus;
8381 goto setstatus;
8382 case NREDIR:
8383 expredir(n->nredir.redirect);
8384 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8385 if (!status) {
8386 evaltree(n->nredir.n, flags & EV_TESTED);
8387 status = exitstatus;
8388 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008389 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008390 goto setstatus;
8391 case NCMD:
8392 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008393 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008394 if (eflag && !(flags & EV_TESTED))
8395 checkexit = ~0;
8396 goto calleval;
8397 case NFOR:
8398 evalfn = evalfor;
8399 goto calleval;
8400 case NWHILE:
8401 case NUNTIL:
8402 evalfn = evalloop;
8403 goto calleval;
8404 case NSUBSHELL:
8405 case NBACKGND:
8406 evalfn = evalsubshell;
8407 goto calleval;
8408 case NPIPE:
8409 evalfn = evalpipe;
8410 goto checkexit;
8411 case NCASE:
8412 evalfn = evalcase;
8413 goto calleval;
8414 case NAND:
8415 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008416 case NSEMI: {
8417
Eric Andersenc470f442003-07-28 09:56:35 +00008418#if NAND + 1 != NOR
8419#error NAND + 1 != NOR
8420#endif
8421#if NOR + 1 != NSEMI
8422#error NOR + 1 != NSEMI
8423#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008424 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008425 evaltree(
8426 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008427 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008428 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008429 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008430 break;
8431 if (!evalskip) {
8432 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008433 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008434 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008435 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008436 evalfn(n, flags);
8437 break;
8438 }
8439 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008440 }
Eric Andersenc470f442003-07-28 09:56:35 +00008441 case NIF:
8442 evaltree(n->nif.test, EV_TESTED);
8443 if (evalskip)
8444 break;
8445 if (exitstatus == 0) {
8446 n = n->nif.ifpart;
8447 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008448 }
8449 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008450 n = n->nif.elsepart;
8451 goto evaln;
8452 }
8453 goto success;
8454 case NDEFUN:
8455 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008456 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008457 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008458 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008459 exitstatus = status;
8460 break;
8461 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008462
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008463 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008464 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008465
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008466 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008467 /* Order of checks below is important:
8468 * signal handlers trigger before exit caused by "set -e".
8469 */
8470 if (pending_sig && dotrap())
8471 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008472 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008473 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008474
8475 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008476 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008477 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008478 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008479
8480 RESTORE_INT(int_level);
8481 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008482}
8483
Eric Andersenc470f442003-07-28 09:56:35 +00008484#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8485static
8486#endif
8487void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8488
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008489static void
Eric Andersenc470f442003-07-28 09:56:35 +00008490evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008491{
8492 int status;
8493
8494 loopnest++;
8495 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008496 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008497 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008498 int i;
8499
Eric Andersencb57d552001-06-28 07:25:16 +00008500 evaltree(n->nbinary.ch1, EV_TESTED);
8501 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008502 skipping:
8503 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008504 evalskip = 0;
8505 continue;
8506 }
8507 if (evalskip == SKIPBREAK && --skipcount <= 0)
8508 evalskip = 0;
8509 break;
8510 }
Eric Andersenc470f442003-07-28 09:56:35 +00008511 i = exitstatus;
8512 if (n->type != NWHILE)
8513 i = !i;
8514 if (i != 0)
8515 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008516 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008517 status = exitstatus;
8518 if (evalskip)
8519 goto skipping;
8520 }
8521 loopnest--;
8522 exitstatus = status;
8523}
8524
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008525static void
Eric Andersenc470f442003-07-28 09:56:35 +00008526evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008527{
8528 struct arglist arglist;
8529 union node *argp;
8530 struct strlist *sp;
8531 struct stackmark smark;
8532
8533 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008534 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008535 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008536 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008537 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008538 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008539 if (evalskip)
8540 goto out;
8541 }
8542 *arglist.lastp = NULL;
8543
8544 exitstatus = 0;
8545 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008546 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008547 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008548 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008549 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008550 if (evalskip) {
8551 if (evalskip == SKIPCONT && --skipcount <= 0) {
8552 evalskip = 0;
8553 continue;
8554 }
8555 if (evalskip == SKIPBREAK && --skipcount <= 0)
8556 evalskip = 0;
8557 break;
8558 }
8559 }
8560 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008561 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008562 popstackmark(&smark);
8563}
8564
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008565static void
Eric Andersenc470f442003-07-28 09:56:35 +00008566evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008567{
8568 union node *cp;
8569 union node *patp;
8570 struct arglist arglist;
8571 struct stackmark smark;
8572
8573 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008574 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008575 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008576 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008577 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008578 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8579 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008580 if (casematch(patp, arglist.list->text)) {
8581 if (evalskip == 0) {
8582 evaltree(cp->nclist.body, flags);
8583 }
8584 goto out;
8585 }
8586 }
8587 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008588 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008589 popstackmark(&smark);
8590}
8591
Eric Andersenc470f442003-07-28 09:56:35 +00008592/*
8593 * Kick off a subshell to evaluate a tree.
8594 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008595static void
Eric Andersenc470f442003-07-28 09:56:35 +00008596evalsubshell(union node *n, int flags)
8597{
8598 struct job *jp;
8599 int backgnd = (n->type == NBACKGND);
8600 int status;
8601
8602 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008603 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008604 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008605 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008606 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008607 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008608 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008609 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008610 flags |= EV_EXIT;
8611 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008612 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008613 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008614 redirect(n->nredir.redirect, 0);
8615 evaltreenr(n->nredir.n, flags);
8616 /* never returns */
8617 }
8618 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008619 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008620 status = waitforjob(jp);
8621 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008622 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008623}
8624
Eric Andersenc470f442003-07-28 09:56:35 +00008625/*
8626 * Compute the names of the files in a redirection list.
8627 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008628static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008629static void
8630expredir(union node *n)
8631{
8632 union node *redir;
8633
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008634 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008635 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008636
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008637 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008638 fn.lastp = &fn.list;
8639 switch (redir->type) {
8640 case NFROMTO:
8641 case NFROM:
8642 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008643#if ENABLE_ASH_BASH_COMPAT
8644 case NTO2:
8645#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008646 case NCLOBBER:
8647 case NAPPEND:
8648 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008649#if ENABLE_ASH_BASH_COMPAT
8650 store_expfname:
8651#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008652 redir->nfile.expfname = fn.list->text;
8653 break;
8654 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008655 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008656 if (redir->ndup.vname) {
8657 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008658 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008659 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008660#if ENABLE_ASH_BASH_COMPAT
8661//FIXME: we used expandarg with different args!
8662 if (!isdigit_str9(fn.list->text)) {
8663 /* >&file, not >&fd */
8664 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8665 ash_msg_and_raise_error("redir error");
8666 redir->type = NTO2;
8667 goto store_expfname;
8668 }
8669#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008670 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008671 }
8672 break;
8673 }
8674 }
8675}
8676
Eric Andersencb57d552001-06-28 07:25:16 +00008677/*
Eric Andersencb57d552001-06-28 07:25:16 +00008678 * Evaluate a pipeline. All the processes in the pipeline are children
8679 * of the process creating the pipeline. (This differs from some versions
8680 * of the shell, which make the last process in a pipeline the parent
8681 * of all the rest.)
8682 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008683static void
Eric Andersenc470f442003-07-28 09:56:35 +00008684evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008685{
8686 struct job *jp;
8687 struct nodelist *lp;
8688 int pipelen;
8689 int prevfd;
8690 int pip[2];
8691
Eric Andersenc470f442003-07-28 09:56:35 +00008692 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008693 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008694 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008695 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008696 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008697 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008698 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008699 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008700 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008701 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008702 pip[1] = -1;
8703 if (lp->next) {
8704 if (pipe(pip) < 0) {
8705 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008706 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008707 }
8708 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008709 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008710 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008711 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008712 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008713 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008714 if (prevfd > 0) {
8715 dup2(prevfd, 0);
8716 close(prevfd);
8717 }
8718 if (pip[1] > 1) {
8719 dup2(pip[1], 1);
8720 close(pip[1]);
8721 }
Eric Andersenc470f442003-07-28 09:56:35 +00008722 evaltreenr(lp->n, flags);
8723 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008724 }
8725 if (prevfd >= 0)
8726 close(prevfd);
8727 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008728 /* Don't want to trigger debugging */
8729 if (pip[1] != -1)
8730 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008731 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008732 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008733 exitstatus = waitforjob(jp);
8734 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008735 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008736 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008737}
8738
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008739/*
8740 * Controls whether the shell is interactive or not.
8741 */
8742static void
8743setinteractive(int on)
8744{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008745 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008746
8747 if (++on == is_interactive)
8748 return;
8749 is_interactive = on;
8750 setsignal(SIGINT);
8751 setsignal(SIGQUIT);
8752 setsignal(SIGTERM);
8753#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8754 if (is_interactive > 1) {
8755 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008756 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008757
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008758 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008759 /* note: ash and hush share this string */
8760 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008761 "Enter 'help' for a list of built-in commands."
8762 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008763 bb_banner,
8764 "built-in shell (ash)"
8765 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008766 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008767 }
8768 }
8769#endif
8770}
8771
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008772static void
8773optschanged(void)
8774{
8775#if DEBUG
8776 opentrace();
8777#endif
8778 setinteractive(iflag);
8779 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008780#if ENABLE_FEATURE_EDITING_VI
8781 if (viflag)
8782 line_input_state->flags |= VI_MODE;
8783 else
8784 line_input_state->flags &= ~VI_MODE;
8785#else
8786 viflag = 0; /* forcibly keep the option off */
8787#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008788}
8789
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008790static struct localvar *localvars;
8791
8792/*
8793 * Called after a function returns.
8794 * Interrupts must be off.
8795 */
8796static void
8797poplocalvars(void)
8798{
8799 struct localvar *lvp;
8800 struct var *vp;
8801
8802 while ((lvp = localvars) != NULL) {
8803 localvars = lvp->next;
8804 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008805 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008806 if (vp == NULL) { /* $- saved */
8807 memcpy(optlist, lvp->text, sizeof(optlist));
8808 free((char*)lvp->text);
8809 optschanged();
8810 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008811 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008812 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008813 if (vp->var_func)
8814 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008815 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008816 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008817 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008818 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008819 }
8820 free(lvp);
8821 }
8822}
8823
8824static int
8825evalfun(struct funcnode *func, int argc, char **argv, int flags)
8826{
8827 volatile struct shparam saveparam;
8828 struct localvar *volatile savelocalvars;
8829 struct jmploc *volatile savehandler;
8830 struct jmploc jmploc;
8831 int e;
8832
8833 saveparam = shellparam;
8834 savelocalvars = localvars;
8835 e = setjmp(jmploc.loc);
8836 if (e) {
8837 goto funcdone;
8838 }
8839 INT_OFF;
8840 savehandler = exception_handler;
8841 exception_handler = &jmploc;
8842 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008843 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008844 func->count++;
8845 funcnest++;
8846 INT_ON;
8847 shellparam.nparam = argc - 1;
8848 shellparam.p = argv + 1;
8849#if ENABLE_ASH_GETOPTS
8850 shellparam.optind = 1;
8851 shellparam.optoff = -1;
8852#endif
8853 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008854 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008855 INT_OFF;
8856 funcnest--;
8857 freefunc(func);
8858 poplocalvars();
8859 localvars = savelocalvars;
8860 freeparam(&shellparam);
8861 shellparam = saveparam;
8862 exception_handler = savehandler;
8863 INT_ON;
8864 evalskip &= ~SKIPFUNC;
8865 return e;
8866}
8867
Denis Vlasenko131ae172007-02-18 13:00:19 +00008868#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008869static char **
8870parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008871{
8872 char *cp, c;
8873
8874 for (;;) {
8875 cp = *++argv;
8876 if (!cp)
8877 return 0;
8878 if (*cp++ != '-')
8879 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008880 c = *cp++;
8881 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008882 break;
8883 if (c == '-' && !*cp) {
8884 argv++;
8885 break;
8886 }
8887 do {
8888 switch (c) {
8889 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008890 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008891 break;
8892 default:
8893 /* run 'typecmd' for other options */
8894 return 0;
8895 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008896 c = *cp++;
8897 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008898 }
8899 return argv;
8900}
8901#endif
8902
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008903/*
8904 * Make a variable a local variable. When a variable is made local, it's
8905 * value and flags are saved in a localvar structure. The saved values
8906 * will be restored when the shell function returns. We handle the name
8907 * "-" as a special case.
8908 */
8909static void
8910mklocal(char *name)
8911{
8912 struct localvar *lvp;
8913 struct var **vpp;
8914 struct var *vp;
8915
8916 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008917 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008918 if (LONE_DASH(name)) {
8919 char *p;
8920 p = ckmalloc(sizeof(optlist));
8921 lvp->text = memcpy(p, optlist, sizeof(optlist));
8922 vp = NULL;
8923 } else {
8924 char *eq;
8925
8926 vpp = hashvar(name);
8927 vp = *findvar(vpp, name);
8928 eq = strchr(name, '=');
8929 if (vp == NULL) {
8930 if (eq)
8931 setvareq(name, VSTRFIXED);
8932 else
8933 setvar(name, NULL, VSTRFIXED);
8934 vp = *vpp; /* the new variable */
8935 lvp->flags = VUNSET;
8936 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008937 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008938 lvp->flags = vp->flags;
8939 vp->flags |= VSTRFIXED|VTEXTFIXED;
8940 if (eq)
8941 setvareq(name, 0);
8942 }
8943 }
8944 lvp->vp = vp;
8945 lvp->next = localvars;
8946 localvars = lvp;
8947 INT_ON;
8948}
8949
8950/*
8951 * The "local" command.
8952 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008953static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008954localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008955{
8956 char *name;
8957
8958 argv = argptr;
8959 while ((name = *argv++) != NULL) {
8960 mklocal(name);
8961 }
8962 return 0;
8963}
8964
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008965static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008966falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008967{
8968 return 1;
8969}
8970
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008971static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008972truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008973{
8974 return 0;
8975}
8976
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008977static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008978execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008979{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008980 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008981 iflag = 0; /* exit on error */
8982 mflag = 0;
8983 optschanged();
8984 shellexec(argv + 1, pathval(), 0);
8985 }
8986 return 0;
8987}
8988
8989/*
8990 * The return command.
8991 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008992static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008993returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008994{
8995 /*
8996 * If called outside a function, do what ksh does;
8997 * skip the rest of the file.
8998 */
8999 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9000 return argv[1] ? number(argv[1]) : exitstatus;
9001}
9002
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009003/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009004static int breakcmd(int, char **) FAST_FUNC;
9005static int dotcmd(int, char **) FAST_FUNC;
9006static int evalcmd(int, char **) FAST_FUNC;
9007static int exitcmd(int, char **) FAST_FUNC;
9008static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009009#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009010static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009011#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009012#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009013static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009014#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009015#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009016static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009017#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009018static int readcmd(int, char **) FAST_FUNC;
9019static int setcmd(int, char **) FAST_FUNC;
9020static int shiftcmd(int, char **) FAST_FUNC;
9021static int timescmd(int, char **) FAST_FUNC;
9022static int trapcmd(int, char **) FAST_FUNC;
9023static int umaskcmd(int, char **) FAST_FUNC;
9024static int unsetcmd(int, char **) FAST_FUNC;
9025static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009026
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009027#define BUILTIN_NOSPEC "0"
9028#define BUILTIN_SPECIAL "1"
9029#define BUILTIN_REGULAR "2"
9030#define BUILTIN_SPEC_REG "3"
9031#define BUILTIN_ASSIGN "4"
9032#define BUILTIN_SPEC_ASSG "5"
9033#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009034#define BUILTIN_SPEC_REG_ASSG "7"
9035
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009036/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009037#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009038static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009039#endif
9040#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009041static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009042#endif
9043#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009044static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009045#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009046
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009047/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009048static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009049 { BUILTIN_SPEC_REG "." , dotcmd },
9050 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009051#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009052 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009053#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009054 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009055#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009056#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009057#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009058 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009059#endif
9060#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009061 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009062#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009063 { BUILTIN_SPEC_REG "break" , breakcmd },
9064 { BUILTIN_REGULAR "cd" , cdcmd },
9065 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009066#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009067 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009068#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009069 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009072#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009073 { BUILTIN_SPEC_REG "eval" , evalcmd },
9074 { BUILTIN_SPEC_REG "exec" , execcmd },
9075 { BUILTIN_SPEC_REG "exit" , exitcmd },
9076 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9077 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009078#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009079 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009080#endif
9081#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009082 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009083#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009084 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009085#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009086 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009087#endif
9088#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009089 { BUILTIN_REGULAR "jobs" , jobscmd },
9090 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009091#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009092#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009093 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009094#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009095 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009096#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009097 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009098#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009099 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9100 { BUILTIN_REGULAR "read" , readcmd },
9101 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9102 { BUILTIN_SPEC_REG "return" , returncmd },
9103 { BUILTIN_SPEC_REG "set" , setcmd },
9104 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009105#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009106 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009107#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009108#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009109 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009110#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009111 { BUILTIN_SPEC_REG "times" , timescmd },
9112 { BUILTIN_SPEC_REG "trap" , trapcmd },
9113 { BUILTIN_REGULAR "true" , truecmd },
9114 { BUILTIN_NOSPEC "type" , typecmd },
9115 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9116 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009117#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009118 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009119#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009120 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9121 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009122};
9123
Denis Vlasenko80591b02008-03-25 07:49:43 +00009124/* Should match the above table! */
9125#define COMMANDCMD (builtintab + \
9126 2 + \
9127 1 * ENABLE_ASH_BUILTIN_TEST + \
9128 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9129 1 * ENABLE_ASH_ALIAS + \
9130 1 * ENABLE_ASH_JOB_CONTROL + \
9131 3)
9132#define EXECCMD (builtintab + \
9133 2 + \
9134 1 * ENABLE_ASH_BUILTIN_TEST + \
9135 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9136 1 * ENABLE_ASH_ALIAS + \
9137 1 * ENABLE_ASH_JOB_CONTROL + \
9138 3 + \
9139 1 * ENABLE_ASH_CMDCMD + \
9140 1 + \
9141 ENABLE_ASH_BUILTIN_ECHO + \
9142 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009143
9144/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009145 * Search the table of builtin commands.
9146 */
9147static struct builtincmd *
9148find_builtin(const char *name)
9149{
9150 struct builtincmd *bp;
9151
9152 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009153 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009154 pstrcmp
9155 );
9156 return bp;
9157}
9158
9159/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009160 * Execute a simple command.
9161 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009162static int
9163isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009164{
9165 const char *q = endofname(p);
9166 if (p == q)
9167 return 0;
9168 return *q == '=';
9169}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009170static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009171bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009172{
9173 /* Preserve exitstatus of a previous possible redirection
9174 * as POSIX mandates */
9175 return back_exitstatus;
9176}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009177static void
Eric Andersenc470f442003-07-28 09:56:35 +00009178evalcommand(union node *cmd, int flags)
9179{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009180 static const struct builtincmd null_bltin = {
9181 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009182 };
Eric Andersenc470f442003-07-28 09:56:35 +00009183 struct stackmark smark;
9184 union node *argp;
9185 struct arglist arglist;
9186 struct arglist varlist;
9187 char **argv;
9188 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009189 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009190 struct cmdentry cmdentry;
9191 struct job *jp;
9192 char *lastarg;
9193 const char *path;
9194 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009195 int status;
9196 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009197 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009198 smallint cmd_is_exec;
9199 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009200
9201 /* First expand the arguments. */
9202 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9203 setstackmark(&smark);
9204 back_exitstatus = 0;
9205
9206 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009207 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009208 varlist.lastp = &varlist.list;
9209 *varlist.lastp = NULL;
9210 arglist.lastp = &arglist.list;
9211 *arglist.lastp = NULL;
9212
9213 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009214 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009215 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9216 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9217 }
9218
Eric Andersenc470f442003-07-28 09:56:35 +00009219 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9220 struct strlist **spp;
9221
9222 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009223 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009224 expandarg(argp, &arglist, EXP_VARTILDE);
9225 else
9226 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9227
Eric Andersenc470f442003-07-28 09:56:35 +00009228 for (sp = *spp; sp; sp = sp->next)
9229 argc++;
9230 }
9231
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009232 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009233 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009234 TRACE(("evalcommand arg: %s\n", sp->text));
9235 *nargv++ = sp->text;
9236 }
9237 *nargv = NULL;
9238
9239 lastarg = NULL;
9240 if (iflag && funcnest == 0 && argc > 0)
9241 lastarg = nargv[-1];
9242
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009243 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009244 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009245 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009246
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009247 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009248 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9249 struct strlist **spp;
9250 char *p;
9251
9252 spp = varlist.lastp;
9253 expandarg(argp, &varlist, EXP_VARTILDE);
9254
9255 /*
9256 * Modify the command lookup path, if a PATH= assignment
9257 * is present
9258 */
9259 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009260 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009261 path = p;
9262 }
9263
9264 /* Print the command if xflag is set. */
9265 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009266 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009267 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009268
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009269 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009270 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009271 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009272 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009273 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009274 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009275 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009276 }
9277 sp = arglist.list;
9278 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009279 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009280 }
9281
9282 cmd_is_exec = 0;
9283 spclbltin = -1;
9284
9285 /* Now locate the command. */
9286 if (argc) {
9287 const char *oldpath;
9288 int cmd_flag = DO_ERR;
9289
9290 path += 5;
9291 oldpath = path;
9292 for (;;) {
9293 find_command(argv[0], &cmdentry, cmd_flag, path);
9294 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009295 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009296 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009297 goto bail;
9298 }
9299
9300 /* implement bltin and command here */
9301 if (cmdentry.cmdtype != CMDBUILTIN)
9302 break;
9303 if (spclbltin < 0)
9304 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9305 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009306 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009307#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009308 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009309 path = oldpath;
9310 nargv = parse_command_args(argv, &path);
9311 if (!nargv)
9312 break;
9313 argc -= nargv - argv;
9314 argv = nargv;
9315 cmd_flag |= DO_NOFUNC;
9316 } else
9317#endif
9318 break;
9319 }
9320 }
9321
9322 if (status) {
9323 /* We have a redirection error. */
9324 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009325 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009326 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009327 exitstatus = status;
9328 goto out;
9329 }
9330
9331 /* Execute the command. */
9332 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009333 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009334
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009335#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009336/* (1) BUG: if variables are set, we need to fork, or save/restore them
9337 * around run_nofork_applet() call.
9338 * (2) Should this check also be done in forkshell()?
9339 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9340 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009341 /* find_command() encodes applet_no as (-2 - applet_no) */
9342 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009343 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009344 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009345 /* run <applet>_main() */
9346 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009347 break;
9348 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009349#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009350 /* Can we avoid forking off? For example, very last command
9351 * in a script or a subshell does not need forking,
9352 * we can just exec it.
9353 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009354 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009355 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009356 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009357 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009358 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009359 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009360 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009361 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009362 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009363 break;
9364 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009365 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009366 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009367 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009368 }
9369 listsetvar(varlist.list, VEXPORT|VSTACK);
9370 shellexec(argv, path, cmdentry.u.index);
9371 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009372 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009373 case CMDBUILTIN:
9374 cmdenviron = varlist.list;
9375 if (cmdenviron) {
9376 struct strlist *list = cmdenviron;
9377 int i = VNOSET;
9378 if (spclbltin > 0 || argc == 0) {
9379 i = 0;
9380 if (cmd_is_exec && argc > 1)
9381 i = VEXPORT;
9382 }
9383 listsetvar(list, i);
9384 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009385 /* Tight loop with builtins only:
9386 * "while kill -0 $child; do true; done"
9387 * will never exit even if $child died, unless we do this
9388 * to reap the zombie and make kill detect that it's gone: */
9389 dowait(DOWAIT_NONBLOCK, NULL);
9390
Eric Andersenc470f442003-07-28 09:56:35 +00009391 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9392 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009393 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009394 if (i == EXEXIT)
9395 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009396 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009397 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009398 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009399 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009400 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009401 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009402 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009403 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009404 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009405 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009406 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009407 }
9408 break;
9409
9410 case CMDFUNCTION:
9411 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009412 /* See above for the rationale */
9413 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009414 if (evalfun(cmdentry.u.func, argc, argv, flags))
9415 goto raise;
9416 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009417
9418 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009419
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009420 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009421 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009422 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009423 /* dsl: I think this is intended to be used to support
9424 * '_' in 'vi' command mode during line editing...
9425 * However I implemented that within libedit itself.
9426 */
9427 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009428 }
Eric Andersenc470f442003-07-28 09:56:35 +00009429 popstackmark(&smark);
9430}
9431
9432static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009433evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9434{
Eric Andersenc470f442003-07-28 09:56:35 +00009435 char *volatile savecmdname;
9436 struct jmploc *volatile savehandler;
9437 struct jmploc jmploc;
9438 int i;
9439
9440 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009441 i = setjmp(jmploc.loc);
9442 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009443 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009444 savehandler = exception_handler;
9445 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009446 commandname = argv[0];
9447 argptr = argv + 1;
9448 optptr = NULL; /* initialize nextopt */
9449 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009450 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009451 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009452 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009453 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009454 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009455 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009456
9457 return i;
9458}
9459
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009460static int
9461goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009462{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009463 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009464}
9465
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009466
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009467/*
9468 * Search for a command. This is called before we fork so that the
9469 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009470 * the child. The check for "goodname" is an overly conservative
9471 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009472 */
Eric Andersenc470f442003-07-28 09:56:35 +00009473static void
9474prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009475{
9476 struct cmdentry entry;
9477
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009478 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9479 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009480}
9481
Eric Andersencb57d552001-06-28 07:25:16 +00009482
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009483/* ============ Builtin commands
9484 *
9485 * Builtin commands whose functions are closely tied to evaluation
9486 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009487 */
9488
9489/*
Eric Andersencb57d552001-06-28 07:25:16 +00009490 * Handle break and continue commands. Break, continue, and return are
9491 * all handled by setting the evalskip flag. The evaluation routines
9492 * above all check this flag, and if it is set they start skipping
9493 * commands rather than executing them. The variable skipcount is
9494 * the number of loops to break/continue, or the number of function
9495 * levels to return. (The latter is always 1.) It should probably
9496 * be an error to break out of more loops than exist, but it isn't
9497 * in the standard shell so we don't make it one here.
9498 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009499static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009500breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009501{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009502 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009503
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009504 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009505 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009506 if (n > loopnest)
9507 n = loopnest;
9508 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009509 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009510 skipcount = n;
9511 }
9512 return 0;
9513}
9514
Eric Andersenc470f442003-07-28 09:56:35 +00009515
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009516/* ============ input.c
9517 *
Eric Andersen90898442003-08-06 11:20:52 +00009518 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009519 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009520
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009521enum {
9522 INPUT_PUSH_FILE = 1,
9523 INPUT_NOFILE_OK = 2,
9524};
Eric Andersencb57d552001-06-28 07:25:16 +00009525
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009526static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009527/* values of checkkwd variable */
9528#define CHKALIAS 0x1
9529#define CHKKWD 0x2
9530#define CHKNL 0x4
9531
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009532/*
9533 * Push a string back onto the input at this current parsefile level.
9534 * We handle aliases this way.
9535 */
9536#if !ENABLE_ASH_ALIAS
9537#define pushstring(s, ap) pushstring(s)
9538#endif
9539static void
9540pushstring(char *s, struct alias *ap)
9541{
9542 struct strpush *sp;
9543 int len;
9544
9545 len = strlen(s);
9546 INT_OFF;
9547 if (g_parsefile->strpush) {
9548 sp = ckzalloc(sizeof(*sp));
9549 sp->prev = g_parsefile->strpush;
9550 } else {
9551 sp = &(g_parsefile->basestrpush);
9552 }
9553 g_parsefile->strpush = sp;
9554 sp->prev_string = g_parsefile->next_to_pgetc;
9555 sp->prev_left_in_line = g_parsefile->left_in_line;
9556#if ENABLE_ASH_ALIAS
9557 sp->ap = ap;
9558 if (ap) {
9559 ap->flag |= ALIASINUSE;
9560 sp->string = s;
9561 }
9562#endif
9563 g_parsefile->next_to_pgetc = s;
9564 g_parsefile->left_in_line = len;
9565 INT_ON;
9566}
9567
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009568static void
9569popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009570{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009571 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009572
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009573 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009574#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009575 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009576 if (g_parsefile->next_to_pgetc[-1] == ' '
9577 || g_parsefile->next_to_pgetc[-1] == '\t'
9578 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009579 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009580 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009581 if (sp->string != sp->ap->val) {
9582 free(sp->string);
9583 }
9584 sp->ap->flag &= ~ALIASINUSE;
9585 if (sp->ap->flag & ALIASDEAD) {
9586 unalias(sp->ap->name);
9587 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009588 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009589#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009590 g_parsefile->next_to_pgetc = sp->prev_string;
9591 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009592 g_parsefile->strpush = sp->prev;
9593 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009594 free(sp);
9595 INT_ON;
9596}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009597
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009598//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9599//it peeks whether it is &>, and then pushes back both chars.
9600//This function needs to save last *next_to_pgetc to buf[0]
9601//to make two pungetc() reliable. Currently,
9602// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009603static int
9604preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009605{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009606 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009607 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009608
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009609 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009610#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009611 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009612 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9613 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009614 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009615 int timeout = -1;
9616# if ENABLE_ASH_IDLE_TIMEOUT
9617 if (iflag) {
9618 const char *tmout_var = lookupvar("TMOUT");
9619 if (tmout_var) {
9620 timeout = atoi(tmout_var) * 1000;
9621 if (timeout <= 0)
9622 timeout = -1;
9623 }
9624 }
9625# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009626# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009627 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009628# endif
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009629 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009630 if (nr == 0) {
9631 /* Ctrl+C pressed */
9632 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009633 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009634 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009635 raise(SIGINT);
9636 return 1;
9637 }
Eric Andersenc470f442003-07-28 09:56:35 +00009638 goto retry;
9639 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009640 if (nr < 0) {
9641 if (errno == 0) {
9642 /* Ctrl+D pressed */
9643 nr = 0;
9644 }
9645# if ENABLE_ASH_IDLE_TIMEOUT
9646 else if (errno == EAGAIN && timeout > 0) {
9647 printf("\007timed out waiting for input: auto-logout\n");
9648 exitshell();
9649 }
9650# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009651 }
Eric Andersencb57d552001-06-28 07:25:16 +00009652 }
9653#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009654 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009655#endif
9656
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009657#if 0 /* disabled: nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009658 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009659 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009660 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009661 if (flags >= 0 && (flags & O_NONBLOCK)) {
9662 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009663 if (fcntl(0, F_SETFL, flags) >= 0) {
9664 out2str("sh: turning off NDELAY mode\n");
9665 goto retry;
9666 }
9667 }
9668 }
9669 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009670#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009671 return nr;
9672}
9673
9674/*
9675 * Refill the input buffer and return the next input character:
9676 *
9677 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009678 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9679 * or we are reading from a string so we can't refill the buffer,
9680 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009681 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009682 * 4) Process input up to the next newline, deleting nul characters.
9683 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009684//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9685#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009686static int
Eric Andersenc470f442003-07-28 09:56:35 +00009687preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009688{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009689 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009690 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009691
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009692 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009693#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009694 if (g_parsefile->left_in_line == -1
9695 && g_parsefile->strpush->ap
9696 && g_parsefile->next_to_pgetc[-1] != ' '
9697 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009698 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009699 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009700 return PEOA;
9701 }
Eric Andersen2870d962001-07-02 17:27:21 +00009702#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009703 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009704 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009705 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9706 g_parsefile->left_in_line,
9707 g_parsefile->next_to_pgetc,
9708 g_parsefile->next_to_pgetc);
9709 if (--g_parsefile->left_in_line >= 0)
9710 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009711 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009712 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009713 * "pgetc" needs refilling.
9714 */
9715
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009716 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009717 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009718 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009719 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009720 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009721 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009722 /* even in failure keep left_in_line and next_to_pgetc
9723 * in lock step, for correct multi-layer pungetc.
9724 * left_in_line was decremented before preadbuffer(),
9725 * must inc next_to_pgetc: */
9726 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009727 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009728 }
Eric Andersencb57d552001-06-28 07:25:16 +00009729
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009730 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009731 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009732 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009733 again:
9734 more = preadfd();
9735 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009736 /* don't try reading again */
9737 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009738 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009739 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009740 return PEOF;
9741 }
9742 }
9743
Denis Vlasenko727752d2008-11-28 03:41:47 +00009744 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009745 * Set g_parsefile->left_in_line
9746 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009747 * NUL chars are deleted.
9748 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009749 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009750 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009751 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009752
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009753 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009754
Denis Vlasenko727752d2008-11-28 03:41:47 +00009755 c = *q;
9756 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009757 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009758 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009759 q++;
9760 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009761 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009762 break;
9763 }
Eric Andersencb57d552001-06-28 07:25:16 +00009764 }
9765
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009766 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009767 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9768 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009769 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009770 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009771 }
9772 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009773 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009774
Eric Andersencb57d552001-06-28 07:25:16 +00009775 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009776 char save = *q;
9777 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009778 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009779 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009780 }
9781
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009782 pgetc_debug("preadbuffer at %d:%p'%s'",
9783 g_parsefile->left_in_line,
9784 g_parsefile->next_to_pgetc,
9785 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009786 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009787}
9788
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009789#define pgetc_as_macro() \
9790 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009791 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009792 : preadbuffer() \
9793 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009794
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009795static int
9796pgetc(void)
9797{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009798 pgetc_debug("pgetc_fast at %d:%p'%s'",
9799 g_parsefile->left_in_line,
9800 g_parsefile->next_to_pgetc,
9801 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009802 return pgetc_as_macro();
9803}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009804
9805#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009806# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009807#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009808# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009809#endif
9810
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009811#if ENABLE_ASH_ALIAS
9812static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009813pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009814{
9815 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009816 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009817 pgetc_debug("pgetc_fast at %d:%p'%s'",
9818 g_parsefile->left_in_line,
9819 g_parsefile->next_to_pgetc,
9820 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009821 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009822 } while (c == PEOA);
9823 return c;
9824}
9825#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009826# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009827#endif
9828
9829/*
9830 * Read a line from the script.
9831 */
9832static char *
9833pfgets(char *line, int len)
9834{
9835 char *p = line;
9836 int nleft = len;
9837 int c;
9838
9839 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009840 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009841 if (c == PEOF) {
9842 if (p == line)
9843 return NULL;
9844 break;
9845 }
9846 *p++ = c;
9847 if (c == '\n')
9848 break;
9849 }
9850 *p = '\0';
9851 return line;
9852}
9853
Eric Andersenc470f442003-07-28 09:56:35 +00009854/*
9855 * Undo the last call to pgetc. Only one character may be pushed back.
9856 * PEOF may be pushed back.
9857 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009858static void
Eric Andersenc470f442003-07-28 09:56:35 +00009859pungetc(void)
9860{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009861 g_parsefile->left_in_line++;
9862 g_parsefile->next_to_pgetc--;
9863 pgetc_debug("pushed back to %d:%p'%s'",
9864 g_parsefile->left_in_line,
9865 g_parsefile->next_to_pgetc,
9866 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009867}
9868
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009869/*
9870 * To handle the "." command, a stack of input files is used. Pushfile
9871 * adds a new entry to the stack and popfile restores the previous level.
9872 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009873static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009874pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009875{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009876 struct parsefile *pf;
9877
Denis Vlasenko597906c2008-02-20 16:38:54 +00009878 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009879 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009880 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009881 /*pf->strpush = NULL; - ckzalloc did it */
9882 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009883 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009884}
9885
9886static void
9887popfile(void)
9888{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009889 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009890
Denis Vlasenkob012b102007-02-19 22:43:01 +00009891 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009892 if (pf->pf_fd >= 0)
9893 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009894 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009895 while (pf->strpush)
9896 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009897 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009898 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009899 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009900}
9901
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009902/*
9903 * Return to top level.
9904 */
9905static void
9906popallfiles(void)
9907{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009908 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009909 popfile();
9910}
9911
9912/*
9913 * Close the file(s) that the shell is reading commands from. Called
9914 * after a fork is done.
9915 */
9916static void
9917closescript(void)
9918{
9919 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009920 if (g_parsefile->pf_fd > 0) {
9921 close(g_parsefile->pf_fd);
9922 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009923 }
9924}
9925
9926/*
9927 * Like setinputfile, but takes an open file descriptor. Call this with
9928 * interrupts off.
9929 */
9930static void
9931setinputfd(int fd, int push)
9932{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009933 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009934 if (push) {
9935 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009936 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009937 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009938 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009939 if (g_parsefile->buf == NULL)
9940 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009941 g_parsefile->left_in_buffer = 0;
9942 g_parsefile->left_in_line = 0;
9943 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009944}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009945
Eric Andersenc470f442003-07-28 09:56:35 +00009946/*
9947 * Set the input to take input from a file. If push is set, push the
9948 * old input onto the stack first.
9949 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009950static int
9951setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009952{
9953 int fd;
9954 int fd2;
9955
Denis Vlasenkob012b102007-02-19 22:43:01 +00009956 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009957 fd = open(fname, O_RDONLY);
9958 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009959 if (flags & INPUT_NOFILE_OK)
9960 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009961 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009962 }
Eric Andersenc470f442003-07-28 09:56:35 +00009963 if (fd < 10) {
9964 fd2 = copyfd(fd, 10);
9965 close(fd);
9966 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009967 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009968 fd = fd2;
9969 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009970 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009971 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009972 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009973 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009974}
9975
Eric Andersencb57d552001-06-28 07:25:16 +00009976/*
9977 * Like setinputfile, but takes input from a string.
9978 */
Eric Andersenc470f442003-07-28 09:56:35 +00009979static void
9980setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009981{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009982 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009983 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009984 g_parsefile->next_to_pgetc = string;
9985 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009986 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009987 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009988 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009989}
9990
9991
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009992/* ============ mail.c
9993 *
9994 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009995 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009996
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009997#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009998
Eric Andersencb57d552001-06-28 07:25:16 +00009999#define MAXMBOXES 10
10000
Eric Andersenc470f442003-07-28 09:56:35 +000010001/* times of mailboxes */
10002static time_t mailtime[MAXMBOXES];
10003/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010004static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010005
Eric Andersencb57d552001-06-28 07:25:16 +000010006/*
Eric Andersenc470f442003-07-28 09:56:35 +000010007 * Print appropriate message(s) if mail has arrived.
10008 * If mail_var_path_changed is set,
10009 * then the value of MAIL has mail_var_path_changed,
10010 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010011 */
Eric Andersenc470f442003-07-28 09:56:35 +000010012static void
10013chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010014{
Eric Andersencb57d552001-06-28 07:25:16 +000010015 const char *mpath;
10016 char *p;
10017 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010018 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010019 struct stackmark smark;
10020 struct stat statb;
10021
Eric Andersencb57d552001-06-28 07:25:16 +000010022 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010023 mpath = mpathset() ? mpathval() : mailval();
10024 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010025 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010026 if (p == NULL)
10027 break;
10028 if (*p == '\0')
10029 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010030 for (q = p; *q; q++)
10031 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010032#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010033 if (q[-1] != '/')
10034 abort();
10035#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010036 q[-1] = '\0'; /* delete trailing '/' */
10037 if (stat(p, &statb) < 0) {
10038 *mtp = 0;
10039 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010040 }
Eric Andersenc470f442003-07-28 09:56:35 +000010041 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10042 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010043 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010044 pathopt ? pathopt : "you have mail"
10045 );
10046 }
10047 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010048 }
Eric Andersenc470f442003-07-28 09:56:35 +000010049 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010050 popstackmark(&smark);
10051}
Eric Andersencb57d552001-06-28 07:25:16 +000010052
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010053static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010054changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010055{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010056 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010057}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010058
Denis Vlasenko131ae172007-02-18 13:00:19 +000010059#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010060
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010061
10062/* ============ ??? */
10063
Eric Andersencb57d552001-06-28 07:25:16 +000010064/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010065 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010066 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010067static void
10068setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010069{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010070 char **newparam;
10071 char **ap;
10072 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010073
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010074 for (nparam = 0; argv[nparam]; nparam++)
10075 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010076 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10077 while (*argv) {
10078 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010079 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010080 *ap = NULL;
10081 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010082 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010083 shellparam.nparam = nparam;
10084 shellparam.p = newparam;
10085#if ENABLE_ASH_GETOPTS
10086 shellparam.optind = 1;
10087 shellparam.optoff = -1;
10088#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010089}
10090
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010091/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010092 * Process shell options. The global variable argptr contains a pointer
10093 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010094 *
10095 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10096 * For a non-interactive shell, an error condition encountered
10097 * by a special built-in ... shall cause the shell to write a diagnostic message
10098 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010099 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010100 * ...
10101 * Utility syntax error (option or operand error) Shall exit
10102 * ...
10103 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10104 * we see that bash does not do that (set "finishes" with error code 1 instead,
10105 * and shell continues), and people rely on this behavior!
10106 * Testcase:
10107 * set -o barfoo 2>/dev/null
10108 * echo $?
10109 *
10110 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010111 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010112static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010113plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010114{
10115 int i;
10116
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010117 if (name) {
10118 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010119 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010120 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010121 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010122 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010123 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010124 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010125 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010126 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010127 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010128 if (val) {
10129 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10130 } else {
10131 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10132 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010133 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010134 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010135}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010136static void
10137setoption(int flag, int val)
10138{
10139 int i;
10140
10141 for (i = 0; i < NOPTS; i++) {
10142 if (optletters(i) == flag) {
10143 optlist[i] = val;
10144 return;
10145 }
10146 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010147 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010148 /* NOTREACHED */
10149}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010150static int
Eric Andersenc470f442003-07-28 09:56:35 +000010151options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010152{
10153 char *p;
10154 int val;
10155 int c;
10156
10157 if (cmdline)
10158 minusc = NULL;
10159 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010160 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010161 if (c != '-' && c != '+')
10162 break;
10163 argptr++;
10164 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010165 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010166 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010167 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010168 if (!cmdline) {
10169 /* "-" means turn off -x and -v */
10170 if (p[0] == '\0')
10171 xflag = vflag = 0;
10172 /* "--" means reset params */
10173 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010174 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010175 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010176 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010177 }
Eric Andersencb57d552001-06-28 07:25:16 +000010178 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010179 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010180 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010181 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010182 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010183 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010184 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010185 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010186 /* it already printed err message */
10187 return 1; /* error */
10188 }
Eric Andersencb57d552001-06-28 07:25:16 +000010189 if (*argptr)
10190 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010191 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10192 isloginsh = 1;
10193 /* bash does not accept +-login, we also won't */
10194 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010195 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010196 isloginsh = 1;
10197 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010198 } else {
10199 setoption(c, val);
10200 }
10201 }
10202 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010203 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010204}
10205
Eric Andersencb57d552001-06-28 07:25:16 +000010206/*
Eric Andersencb57d552001-06-28 07:25:16 +000010207 * The shift builtin command.
10208 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010209static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010210shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010211{
10212 int n;
10213 char **ap1, **ap2;
10214
10215 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010216 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010217 n = number(argv[1]);
10218 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010219 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010220 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010221 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010222 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010223 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010224 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010225 }
10226 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010227 while ((*ap2++ = *ap1++) != NULL)
10228 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010229#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010230 shellparam.optind = 1;
10231 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010232#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010233 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010234 return 0;
10235}
10236
Eric Andersencb57d552001-06-28 07:25:16 +000010237/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010238 * POSIX requires that 'set' (but not export or readonly) output the
10239 * variables in lexicographic order - by the locale's collating order (sigh).
10240 * Maybe we could keep them in an ordered balanced binary tree
10241 * instead of hashed lists.
10242 * For now just roll 'em through qsort for printing...
10243 */
10244static int
10245showvars(const char *sep_prefix, int on, int off)
10246{
10247 const char *sep;
10248 char **ep, **epend;
10249
10250 ep = listvars(on, off, &epend);
10251 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10252
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010253 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010254
10255 for (; ep < epend; ep++) {
10256 const char *p;
10257 const char *q;
10258
10259 p = strchrnul(*ep, '=');
10260 q = nullstr;
10261 if (*p)
10262 q = single_quote(++p);
10263 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10264 }
10265 return 0;
10266}
10267
10268/*
Eric Andersencb57d552001-06-28 07:25:16 +000010269 * The set command builtin.
10270 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010271static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010272setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010273{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010274 int retval;
10275
Denis Vlasenko68404f12008-03-17 09:00:54 +000010276 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010277 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010278
Denis Vlasenkob012b102007-02-19 22:43:01 +000010279 INT_OFF;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010280 retval = options(/*cmdline:*/ 0);
10281 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010282 optschanged();
10283 if (*argptr != NULL) {
10284 setparam(argptr);
10285 }
Eric Andersencb57d552001-06-28 07:25:16 +000010286 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010287 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010288 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010289}
10290
Denis Vlasenko131ae172007-02-18 13:00:19 +000010291#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010292static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010293change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010294{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010295 uint32_t t;
10296
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010297 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010298 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010299 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010300 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010301 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010302 vrandom.flags &= ~VNOFUNC;
10303 } else {
10304 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010305 t = strtoul(value, NULL, 10);
10306 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010307 }
Eric Andersenef02f822004-03-11 13:34:24 +000010308}
Eric Andersen16767e22004-03-16 05:14:10 +000010309#endif
10310
Denis Vlasenko131ae172007-02-18 13:00:19 +000010311#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010312static int
Eric Andersenc470f442003-07-28 09:56:35 +000010313getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010314{
10315 char *p, *q;
10316 char c = '?';
10317 int done = 0;
10318 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010319 char s[12];
10320 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010321
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010322 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010323 return 1;
10324 optnext = optfirst + *param_optind - 1;
10325
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010326 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010327 p = NULL;
10328 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010329 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010330 if (p == NULL || *p == '\0') {
10331 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010332 p = *optnext;
10333 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010334 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010335 p = NULL;
10336 done = 1;
10337 goto out;
10338 }
10339 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010340 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010341 goto atend;
10342 }
10343
10344 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010345 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010346 if (*q == '\0') {
10347 if (optstr[0] == ':') {
10348 s[0] = c;
10349 s[1] = '\0';
10350 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010351 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010352 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010353 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010354 }
10355 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010356 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010357 }
10358 if (*++q == ':')
10359 q++;
10360 }
10361
10362 if (*++q == ':') {
10363 if (*p == '\0' && (p = *optnext) == NULL) {
10364 if (optstr[0] == ':') {
10365 s[0] = c;
10366 s[1] = '\0';
10367 err |= setvarsafe("OPTARG", s, 0);
10368 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010369 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010370 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010371 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010372 c = '?';
10373 }
Eric Andersenc470f442003-07-28 09:56:35 +000010374 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010375 }
10376
10377 if (p == *optnext)
10378 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010379 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010380 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010381 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010382 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010383 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010384 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010385 *param_optind = optnext - optfirst + 1;
10386 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010387 err |= setvarsafe("OPTIND", s, VNOFUNC);
10388 s[0] = c;
10389 s[1] = '\0';
10390 err |= setvarsafe(optvar, s, 0);
10391 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010392 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010393 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010394 flush_stdout_stderr();
10395 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010396 }
10397 return done;
10398}
Eric Andersenc470f442003-07-28 09:56:35 +000010399
10400/*
10401 * The getopts builtin. Shellparam.optnext points to the next argument
10402 * to be processed. Shellparam.optptr points to the next character to
10403 * be processed in the current argument. If shellparam.optnext is NULL,
10404 * then it's the first time getopts has been called.
10405 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010406static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010407getoptscmd(int argc, char **argv)
10408{
10409 char **optbase;
10410
10411 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010412 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010413 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010414 optbase = shellparam.p;
10415 if (shellparam.optind > shellparam.nparam + 1) {
10416 shellparam.optind = 1;
10417 shellparam.optoff = -1;
10418 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010419 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010420 optbase = &argv[3];
10421 if (shellparam.optind > argc - 2) {
10422 shellparam.optind = 1;
10423 shellparam.optoff = -1;
10424 }
10425 }
10426
10427 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010428 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010429}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010430#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010431
Eric Andersencb57d552001-06-28 07:25:16 +000010432
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010433/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010434
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010435struct heredoc {
10436 struct heredoc *next; /* next here document in list */
10437 union node *here; /* redirection node */
10438 char *eofmark; /* string indicating end of input */
10439 smallint striptabs; /* if set, strip leading tabs */
10440};
10441
10442static smallint tokpushback; /* last token pushed back */
10443static smallint parsebackquote; /* nonzero if we are inside backquotes */
10444static smallint quoteflag; /* set if (part of) last token was quoted */
10445static token_id_t lasttoken; /* last token read (integer id Txxx) */
10446static struct heredoc *heredoclist; /* list of here documents to read */
10447static char *wordtext; /* text of last word returned by readtoken */
10448static struct nodelist *backquotelist;
10449static union node *redirnode;
10450static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010451
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010452static const char *
10453tokname(char *buf, int tok)
10454{
10455 if (tok < TSEMI)
10456 return tokname_array[tok] + 1;
10457 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10458 return buf;
10459}
10460
10461/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010462 * Called when an unexpected token is read during the parse. The argument
10463 * is the token that is expected, or -1 if more than one type of token can
10464 * occur at this point.
10465 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010466static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010467static void
10468raise_error_unexpected_syntax(int token)
10469{
10470 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010471 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010472 int l;
10473
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010474 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010475 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010476 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010477 raise_error_syntax(msg);
10478 /* NOTREACHED */
10479}
Eric Andersencb57d552001-06-28 07:25:16 +000010480
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010481#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010482
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010483/* parsing is heavily cross-recursive, need these forward decls */
10484static union node *andor(void);
10485static union node *pipeline(void);
10486static union node *parse_command(void);
10487static void parseheredoc(void);
10488static char peektoken(void);
10489static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010490
Eric Andersenc470f442003-07-28 09:56:35 +000010491static union node *
10492list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010493{
10494 union node *n1, *n2, *n3;
10495 int tok;
10496
Eric Andersenc470f442003-07-28 09:56:35 +000010497 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10498 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010499 return NULL;
10500 n1 = NULL;
10501 for (;;) {
10502 n2 = andor();
10503 tok = readtoken();
10504 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010505 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010506 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010507 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010508 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010509 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010510 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010511 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010512 n2 = n3;
10513 }
10514 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010515 }
10516 }
10517 if (n1 == NULL) {
10518 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010519 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010520 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010521 n3->type = NSEMI;
10522 n3->nbinary.ch1 = n1;
10523 n3->nbinary.ch2 = n2;
10524 n1 = n3;
10525 }
10526 switch (tok) {
10527 case TBACKGND:
10528 case TSEMI:
10529 tok = readtoken();
10530 /* fall through */
10531 case TNL:
10532 if (tok == TNL) {
10533 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010534 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010535 return n1;
10536 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010537 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010538 }
Eric Andersenc470f442003-07-28 09:56:35 +000010539 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010540 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010541 return n1;
10542 break;
10543 case TEOF:
10544 if (heredoclist)
10545 parseheredoc();
10546 else
Eric Andersenc470f442003-07-28 09:56:35 +000010547 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010548 return n1;
10549 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010550 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010551 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010552 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010553 return n1;
10554 }
10555 }
10556}
10557
Eric Andersenc470f442003-07-28 09:56:35 +000010558static union node *
10559andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010560{
Eric Andersencb57d552001-06-28 07:25:16 +000010561 union node *n1, *n2, *n3;
10562 int t;
10563
Eric Andersencb57d552001-06-28 07:25:16 +000010564 n1 = pipeline();
10565 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010566 t = readtoken();
10567 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010568 t = NAND;
10569 } else if (t == TOR) {
10570 t = NOR;
10571 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010572 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010573 return n1;
10574 }
Eric Andersenc470f442003-07-28 09:56:35 +000010575 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010576 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010577 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010578 n3->type = t;
10579 n3->nbinary.ch1 = n1;
10580 n3->nbinary.ch2 = n2;
10581 n1 = n3;
10582 }
10583}
10584
Eric Andersenc470f442003-07-28 09:56:35 +000010585static union node *
10586pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010587{
Eric Andersencb57d552001-06-28 07:25:16 +000010588 union node *n1, *n2, *pipenode;
10589 struct nodelist *lp, *prev;
10590 int negate;
10591
10592 negate = 0;
10593 TRACE(("pipeline: entered\n"));
10594 if (readtoken() == TNOT) {
10595 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010596 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010597 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010598 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010599 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010600 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010601 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010602 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010603 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010604 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010605 pipenode->npipe.cmdlist = lp;
10606 lp->n = n1;
10607 do {
10608 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010609 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010610 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010611 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010612 prev->next = lp;
10613 } while (readtoken() == TPIPE);
10614 lp->next = NULL;
10615 n1 = pipenode;
10616 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010617 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010618 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010619 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010620 n2->type = NNOT;
10621 n2->nnot.com = n1;
10622 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010623 }
10624 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010625}
10626
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010627static union node *
10628makename(void)
10629{
10630 union node *n;
10631
Denis Vlasenko597906c2008-02-20 16:38:54 +000010632 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010633 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010634 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010635 n->narg.text = wordtext;
10636 n->narg.backquote = backquotelist;
10637 return n;
10638}
10639
10640static void
10641fixredir(union node *n, const char *text, int err)
10642{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010643 int fd;
10644
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010645 TRACE(("Fix redir %s %d\n", text, err));
10646 if (!err)
10647 n->ndup.vname = NULL;
10648
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010649 fd = bb_strtou(text, NULL, 10);
10650 if (!errno && fd >= 0)
10651 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010652 else if (LONE_DASH(text))
10653 n->ndup.dupfd = -1;
10654 else {
10655 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010656 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010657 n->ndup.vname = makename();
10658 }
10659}
10660
10661/*
10662 * Returns true if the text contains nothing to expand (no dollar signs
10663 * or backquotes).
10664 */
10665static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010666noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010667{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010668 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010669
Denys Vlasenkocd716832009-11-28 22:14:02 +010010670 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010671 if (c == CTLQUOTEMARK)
10672 continue;
10673 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010674 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010675 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010676 return 0;
10677 }
10678 return 1;
10679}
10680
10681static void
10682parsefname(void)
10683{
10684 union node *n = redirnode;
10685
10686 if (readtoken() != TWORD)
10687 raise_error_unexpected_syntax(-1);
10688 if (n->type == NHERE) {
10689 struct heredoc *here = heredoc;
10690 struct heredoc *p;
10691 int i;
10692
10693 if (quoteflag == 0)
10694 n->type = NXHERE;
10695 TRACE(("Here document %d\n", n->type));
10696 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010697 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010698 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010699 here->eofmark = wordtext;
10700 here->next = NULL;
10701 if (heredoclist == NULL)
10702 heredoclist = here;
10703 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010704 for (p = heredoclist; p->next; p = p->next)
10705 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010706 p->next = here;
10707 }
10708 } else if (n->type == NTOFD || n->type == NFROMFD) {
10709 fixredir(n, wordtext, 0);
10710 } else {
10711 n->nfile.fname = makename();
10712 }
10713}
Eric Andersencb57d552001-06-28 07:25:16 +000010714
Eric Andersenc470f442003-07-28 09:56:35 +000010715static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010716simplecmd(void)
10717{
10718 union node *args, **app;
10719 union node *n = NULL;
10720 union node *vars, **vpp;
10721 union node **rpp, *redir;
10722 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010723#if ENABLE_ASH_BASH_COMPAT
10724 smallint double_brackets_flag = 0;
10725#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010726
10727 args = NULL;
10728 app = &args;
10729 vars = NULL;
10730 vpp = &vars;
10731 redir = NULL;
10732 rpp = &redir;
10733
10734 savecheckkwd = CHKALIAS;
10735 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010736 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010737 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010738 t = readtoken();
10739 switch (t) {
10740#if ENABLE_ASH_BASH_COMPAT
10741 case TAND: /* "&&" */
10742 case TOR: /* "||" */
10743 if (!double_brackets_flag) {
10744 tokpushback = 1;
10745 goto out;
10746 }
10747 wordtext = (char *) (t == TAND ? "-a" : "-o");
10748#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010749 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010750 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010751 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010752 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010753 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010754#if ENABLE_ASH_BASH_COMPAT
10755 if (strcmp("[[", wordtext) == 0)
10756 double_brackets_flag = 1;
10757 else if (strcmp("]]", wordtext) == 0)
10758 double_brackets_flag = 0;
10759#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010760 n->narg.backquote = backquotelist;
10761 if (savecheckkwd && isassignment(wordtext)) {
10762 *vpp = n;
10763 vpp = &n->narg.next;
10764 } else {
10765 *app = n;
10766 app = &n->narg.next;
10767 savecheckkwd = 0;
10768 }
10769 break;
10770 case TREDIR:
10771 *rpp = n = redirnode;
10772 rpp = &n->nfile.next;
10773 parsefname(); /* read name of redirection file */
10774 break;
10775 case TLP:
10776 if (args && app == &args->narg.next
10777 && !vars && !redir
10778 ) {
10779 struct builtincmd *bcmd;
10780 const char *name;
10781
10782 /* We have a function */
10783 if (readtoken() != TRP)
10784 raise_error_unexpected_syntax(TRP);
10785 name = n->narg.text;
10786 if (!goodname(name)
10787 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10788 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010789 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010790 }
10791 n->type = NDEFUN;
10792 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10793 n->narg.next = parse_command();
10794 return n;
10795 }
10796 /* fall through */
10797 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010798 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010799 goto out;
10800 }
10801 }
10802 out:
10803 *app = NULL;
10804 *vpp = NULL;
10805 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010806 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010807 n->type = NCMD;
10808 n->ncmd.args = args;
10809 n->ncmd.assign = vars;
10810 n->ncmd.redirect = redir;
10811 return n;
10812}
10813
10814static union node *
10815parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010816{
Eric Andersencb57d552001-06-28 07:25:16 +000010817 union node *n1, *n2;
10818 union node *ap, **app;
10819 union node *cp, **cpp;
10820 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010821 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010822 int t;
10823
10824 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010825 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010826
Eric Andersencb57d552001-06-28 07:25:16 +000010827 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010828 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010829 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010830 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010831 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010832 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010833 n1->type = NIF;
10834 n1->nif.test = list(0);
10835 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010836 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010837 n1->nif.ifpart = list(0);
10838 n2 = n1;
10839 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010840 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010841 n2 = n2->nif.elsepart;
10842 n2->type = NIF;
10843 n2->nif.test = list(0);
10844 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010845 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010846 n2->nif.ifpart = list(0);
10847 }
10848 if (lasttoken == TELSE)
10849 n2->nif.elsepart = list(0);
10850 else {
10851 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010852 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010853 }
Eric Andersenc470f442003-07-28 09:56:35 +000010854 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010855 break;
10856 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010857 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010858 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010859 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010860 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010861 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010862 got = readtoken();
10863 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010864 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010865 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010866 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010867 }
10868 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010869 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010870 break;
10871 }
10872 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010873 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010874 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010875 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010876 n1->type = NFOR;
10877 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010878 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010879 if (readtoken() == TIN) {
10880 app = &ap;
10881 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010882 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010883 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010884 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010885 n2->narg.text = wordtext;
10886 n2->narg.backquote = backquotelist;
10887 *app = n2;
10888 app = &n2->narg.next;
10889 }
10890 *app = NULL;
10891 n1->nfor.args = ap;
10892 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010893 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010894 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010895 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010896 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010897 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010898 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010899 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010900 n1->nfor.args = n2;
10901 /*
10902 * Newline or semicolon here is optional (but note
10903 * that the original Bourne shell only allowed NL).
10904 */
10905 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010906 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010907 }
Eric Andersenc470f442003-07-28 09:56:35 +000010908 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010909 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010910 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010911 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010912 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010913 break;
10914 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010915 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010916 n1->type = NCASE;
10917 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010918 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010919 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010920 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010921 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010922 n2->narg.text = wordtext;
10923 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010924 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010925 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010926 } while (readtoken() == TNL);
10927 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010928 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010929 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010930 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010931 checkkwd = CHKNL | CHKKWD;
10932 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010933 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010934 if (lasttoken == TLP)
10935 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010936 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010937 cp->type = NCLIST;
10938 app = &cp->nclist.pattern;
10939 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010940 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010941 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010942 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010943 ap->narg.text = wordtext;
10944 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010945 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010946 break;
10947 app = &ap->narg.next;
10948 readtoken();
10949 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010950 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010951 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010952 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010953 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010954
Eric Andersenc470f442003-07-28 09:56:35 +000010955 cpp = &cp->nclist.next;
10956
10957 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010958 t = readtoken();
10959 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010960 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010961 raise_error_unexpected_syntax(TENDCASE);
10962 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010963 }
Eric Andersenc470f442003-07-28 09:56:35 +000010964 }
Eric Andersencb57d552001-06-28 07:25:16 +000010965 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010966 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010967 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010968 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010969 n1->type = NSUBSHELL;
10970 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010971 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010972 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010973 break;
10974 case TBEGIN:
10975 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010976 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010977 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010978 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010979 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010980 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010981 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010982 }
10983
Eric Andersenc470f442003-07-28 09:56:35 +000010984 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010985 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010986
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010987 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010988 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010989 checkkwd = CHKKWD | CHKALIAS;
10990 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010991 while (readtoken() == TREDIR) {
10992 *rpp = n2 = redirnode;
10993 rpp = &n2->nfile.next;
10994 parsefname();
10995 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010996 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010997 *rpp = NULL;
10998 if (redir) {
10999 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011000 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011001 n2->type = NREDIR;
11002 n2->nredir.n = n1;
11003 n1 = n2;
11004 }
11005 n1->nredir.redirect = redir;
11006 }
Eric Andersencb57d552001-06-28 07:25:16 +000011007 return n1;
11008}
11009
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011010#if ENABLE_ASH_BASH_COMPAT
11011static int decode_dollar_squote(void)
11012{
11013 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11014 int c, cnt;
11015 char *p;
11016 char buf[4];
11017
11018 c = pgetc();
11019 p = strchr(C_escapes, c);
11020 if (p) {
11021 buf[0] = c;
11022 p = buf;
11023 cnt = 3;
11024 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11025 do {
11026 c = pgetc();
11027 *++p = c;
11028 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11029 pungetc();
11030 } else if (c == 'x') { /* \xHH */
11031 do {
11032 c = pgetc();
11033 *++p = c;
11034 } while (isxdigit(c) && --cnt);
11035 pungetc();
11036 if (cnt == 3) { /* \x but next char is "bad" */
11037 c = 'x';
11038 goto unrecognized;
11039 }
11040 } else { /* simple seq like \\ or \t */
11041 p++;
11042 }
11043 *p = '\0';
11044 p = buf;
11045 c = bb_process_escape_sequence((void*)&p);
11046 } else { /* unrecognized "\z": print both chars unless ' or " */
11047 if (c != '\'' && c != '"') {
11048 unrecognized:
11049 c |= 0x100; /* "please encode \, then me" */
11050 }
11051 }
11052 return c;
11053}
11054#endif
11055
Eric Andersencb57d552001-06-28 07:25:16 +000011056/*
11057 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11058 * is not NULL, read a here document. In the latter case, eofmark is the
11059 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011060 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011061 * is the first character of the input token or document.
11062 *
11063 * Because C does not have internal subroutines, I have simulated them
11064 * using goto's to implement the subroutine linkage. The following macros
11065 * will run code that appears at the end of readtoken1.
11066 */
Eric Andersen2870d962001-07-02 17:27:21 +000011067#define CHECKEND() {goto checkend; checkend_return:;}
11068#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11069#define PARSESUB() {goto parsesub; parsesub_return:;}
11070#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11071#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11072#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011073static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011074readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011075{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011076 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011077 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011078 char *out;
11079 int len;
11080 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011081 struct nodelist *bqlist;
11082 smallint quotef;
11083 smallint dblquote;
11084 smallint oldstyle;
11085 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011086#if ENABLE_ASH_EXPAND_PRMT
11087 smallint pssyntax; /* we are expanding a prompt string */
11088#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011089 int varnest; /* levels of variables expansion */
11090 int arinest; /* levels of arithmetic expansion */
11091 int parenlevel; /* levels of parens in arithmetic */
11092 int dqvarnest; /* levels of variables expansion within double quotes */
11093
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011094 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011095
Eric Andersencb57d552001-06-28 07:25:16 +000011096#if __GNUC__
11097 /* Avoid longjmp clobbering */
11098 (void) &out;
11099 (void) &quotef;
11100 (void) &dblquote;
11101 (void) &varnest;
11102 (void) &arinest;
11103 (void) &parenlevel;
11104 (void) &dqvarnest;
11105 (void) &oldstyle;
11106 (void) &prevsyntax;
11107 (void) &syntax;
11108#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011109 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011110 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011111 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011112 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011113#if ENABLE_ASH_EXPAND_PRMT
11114 pssyntax = (syntax == PSSYNTAX);
11115 if (pssyntax)
11116 syntax = DQSYNTAX;
11117#endif
11118 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011119 varnest = 0;
11120 arinest = 0;
11121 parenlevel = 0;
11122 dqvarnest = 0;
11123
11124 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011125 loop:
11126 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011127 CHECKEND(); /* set c to PEOF if at end of here document */
11128 for (;;) { /* until end of line or end of word */
11129 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11130 switch (SIT(c, syntax)) {
11131 case CNL: /* '\n' */
11132 if (syntax == BASESYNTAX)
11133 goto endword; /* exit outer loop */
11134 USTPUTC(c, out);
11135 g_parsefile->linno++;
11136 setprompt_if(doprompt, 2);
11137 c = pgetc();
11138 goto loop; /* continue outer loop */
11139 case CWORD:
11140 USTPUTC(c, out);
11141 break;
11142 case CCTL:
11143 if (eofmark == NULL || dblquote)
11144 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011145#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011146 if (c == '\\' && bash_dollar_squote) {
11147 c = decode_dollar_squote();
11148 if (c & 0x100) {
11149 USTPUTC('\\', out);
11150 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011151 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011152 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011153#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011154 USTPUTC(c, out);
11155 break;
11156 case CBACK: /* backslash */
11157 c = pgetc_without_PEOA();
11158 if (c == PEOF) {
11159 USTPUTC(CTLESC, out);
11160 USTPUTC('\\', out);
11161 pungetc();
11162 } else if (c == '\n') {
11163 setprompt_if(doprompt, 2);
11164 } else {
11165#if ENABLE_ASH_EXPAND_PRMT
11166 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011167 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011168 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011169 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011170#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011171 /* Backslash is retained if we are in "str" and next char isn't special */
11172 if (dblquote
11173 && c != '\\'
11174 && c != '`'
11175 && c != '$'
11176 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011177 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011178 USTPUTC(CTLESC, out);
11179 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011180 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011181 if (SIT(c, SQSYNTAX) == CCTL)
11182 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011183 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011184 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011185 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011186 break;
11187 case CSQUOTE:
11188 syntax = SQSYNTAX;
11189 quotemark:
11190 if (eofmark == NULL) {
11191 USTPUTC(CTLQUOTEMARK, out);
11192 }
11193 break;
11194 case CDQUOTE:
11195 syntax = DQSYNTAX;
11196 dblquote = 1;
11197 goto quotemark;
11198 case CENDQUOTE:
11199 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11200 if (eofmark != NULL && arinest == 0
11201 && varnest == 0
11202 ) {
11203 USTPUTC(c, out);
11204 } else {
11205 if (dqvarnest == 0) {
11206 syntax = BASESYNTAX;
11207 dblquote = 0;
11208 }
11209 quotef = 1;
11210 goto quotemark;
11211 }
11212 break;
11213 case CVAR: /* '$' */
11214 PARSESUB(); /* parse substitution */
11215 break;
11216 case CENDVAR: /* '}' */
11217 if (varnest > 0) {
11218 varnest--;
11219 if (dqvarnest > 0) {
11220 dqvarnest--;
11221 }
11222 c = CTLENDVAR;
11223 }
11224 USTPUTC(c, out);
11225 break;
11226#if ENABLE_SH_MATH_SUPPORT
11227 case CLP: /* '(' in arithmetic */
11228 parenlevel++;
11229 USTPUTC(c, out);
11230 break;
11231 case CRP: /* ')' in arithmetic */
11232 if (parenlevel > 0) {
11233 parenlevel--;
11234 } else {
11235 if (pgetc() == ')') {
11236 if (--arinest == 0) {
11237 syntax = prevsyntax;
11238 dblquote = (syntax == DQSYNTAX);
11239 c = CTLENDARI;
11240 }
11241 } else {
11242 /*
11243 * unbalanced parens
11244 * (don't 2nd guess - no error)
11245 */
11246 pungetc();
11247 }
11248 }
11249 USTPUTC(c, out);
11250 break;
11251#endif
11252 case CBQUOTE: /* '`' */
11253 PARSEBACKQOLD();
11254 break;
11255 case CENDFILE:
11256 goto endword; /* exit outer loop */
11257 case CIGN:
11258 break;
11259 default:
11260 if (varnest == 0) {
11261#if ENABLE_ASH_BASH_COMPAT
11262 if (c == '&') {
11263 if (pgetc() == '>')
11264 c = 0x100 + '>'; /* flag &> */
11265 pungetc();
11266 }
11267#endif
11268 goto endword; /* exit outer loop */
11269 }
11270 IF_ASH_ALIAS(if (c != PEOA))
11271 USTPUTC(c, out);
11272 }
11273 c = pgetc_fast();
11274 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011275 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011276
Mike Frysinger98c52642009-04-02 10:02:37 +000011277#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011278 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011279 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011280#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011281 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011282 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011283 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011284 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011285 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011286 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011287 }
11288 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011289 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011290 out = stackblock();
11291 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011292 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011293 && quotef == 0
11294 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011295 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011296 PARSEREDIR(); /* passed as params: out, c */
11297 lasttoken = TREDIR;
11298 return lasttoken;
11299 }
11300 /* else: non-number X seen, interpret it
11301 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011302 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011303 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011304 }
11305 quoteflag = quotef;
11306 backquotelist = bqlist;
11307 grabstackblock(len);
11308 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011309 lasttoken = TWORD;
11310 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011311/* end of readtoken routine */
11312
Eric Andersencb57d552001-06-28 07:25:16 +000011313/*
11314 * Check to see whether we are at the end of the here document. When this
11315 * is called, c is set to the first character of the next input line. If
11316 * we are at the end of the here document, this routine sets the c to PEOF.
11317 */
Eric Andersenc470f442003-07-28 09:56:35 +000011318checkend: {
11319 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011320#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011321 if (c == PEOA)
11322 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011323#endif
11324 if (striptabs) {
11325 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011326 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011327 }
Eric Andersenc470f442003-07-28 09:56:35 +000011328 }
11329 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011330 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011331 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011332
Eric Andersenc470f442003-07-28 09:56:35 +000011333 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011334 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11335 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011336 if (*p == '\n' && *q == '\0') {
11337 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011338 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011339 needprompt = doprompt;
11340 } else {
11341 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011342 }
11343 }
11344 }
11345 }
Eric Andersenc470f442003-07-28 09:56:35 +000011346 goto checkend_return;
11347}
Eric Andersencb57d552001-06-28 07:25:16 +000011348
Eric Andersencb57d552001-06-28 07:25:16 +000011349/*
11350 * Parse a redirection operator. The variable "out" points to a string
11351 * specifying the fd to be redirected. The variable "c" contains the
11352 * first character of the redirection operator.
11353 */
Eric Andersenc470f442003-07-28 09:56:35 +000011354parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011355 /* out is already checked to be a valid number or "" */
11356 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011357 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011358
Denis Vlasenko597906c2008-02-20 16:38:54 +000011359 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011360 if (c == '>') {
11361 np->nfile.fd = 1;
11362 c = pgetc();
11363 if (c == '>')
11364 np->type = NAPPEND;
11365 else if (c == '|')
11366 np->type = NCLOBBER;
11367 else if (c == '&')
11368 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011369 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011370 else {
11371 np->type = NTO;
11372 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011373 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011374 }
11375#if ENABLE_ASH_BASH_COMPAT
11376 else if (c == 0x100 + '>') { /* this flags &> redirection */
11377 np->nfile.fd = 1;
11378 pgetc(); /* this is '>', no need to check */
11379 np->type = NTO2;
11380 }
11381#endif
11382 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011383 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011384 c = pgetc();
11385 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011386 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011387 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011388 np = stzalloc(sizeof(struct nhere));
11389 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011390 }
11391 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011392 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011393 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011394 c = pgetc();
11395 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011396 heredoc->striptabs = 1;
11397 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011398 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011399 pungetc();
11400 }
11401 break;
11402
11403 case '&':
11404 np->type = NFROMFD;
11405 break;
11406
11407 case '>':
11408 np->type = NFROMTO;
11409 break;
11410
11411 default:
11412 np->type = NFROM;
11413 pungetc();
11414 break;
11415 }
Eric Andersencb57d552001-06-28 07:25:16 +000011416 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011417 if (fd >= 0)
11418 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011419 redirnode = np;
11420 goto parseredir_return;
11421}
Eric Andersencb57d552001-06-28 07:25:16 +000011422
Eric Andersencb57d552001-06-28 07:25:16 +000011423/*
11424 * Parse a substitution. At this point, we have read the dollar sign
11425 * and nothing else.
11426 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011427
11428/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11429 * (assuming ascii char codes, as the original implementation did) */
11430#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011431 (((unsigned)(c) - 33 < 32) \
11432 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011433parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011434 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011435 int typeloc;
11436 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011437
Eric Andersenc470f442003-07-28 09:56:35 +000011438 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011439 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011440 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011441 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011442#if ENABLE_ASH_BASH_COMPAT
11443 if (c == '\'')
11444 bash_dollar_squote = 1;
11445 else
11446#endif
11447 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011448 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011449 } else if (c == '(') {
11450 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011451 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011452#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011453 PARSEARITH();
11454#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011455 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011456#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011457 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011458 pungetc();
11459 PARSEBACKQNEW();
11460 }
11461 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011462 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011463 USTPUTC(CTLVAR, out);
11464 typeloc = out - (char *)stackblock();
11465 USTPUTC(VSNORMAL, out);
11466 subtype = VSNORMAL;
11467 if (c == '{') {
11468 c = pgetc();
11469 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011470 c = pgetc();
11471 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011472 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011473 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011474 subtype = VSLENGTH; /* ${#VAR} */
11475 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011476 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011477 }
Eric Andersenc470f442003-07-28 09:56:35 +000011478 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011479 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011480 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011481 do {
11482 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011483 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011484 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011485 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011486 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011487 do {
11488 STPUTC(c, out);
11489 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011490 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011491 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011492 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011493 USTPUTC(c, out);
11494 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011495 } else {
11496 badsub:
11497 raise_error_syntax("bad substitution");
11498 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011499 if (c != '}' && subtype == VSLENGTH) {
11500 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011501 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011502 }
Eric Andersencb57d552001-06-28 07:25:16 +000011503
Eric Andersenc470f442003-07-28 09:56:35 +000011504 STPUTC('=', out);
11505 flags = 0;
11506 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011507 /* ${VAR...} but not $VAR or ${#VAR} */
11508 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011509 switch (c) {
11510 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011511 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011512#if ENABLE_ASH_BASH_COMPAT
11513 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011514//TODO: support more general format ${v:EXPR:EXPR},
11515// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011516 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011517 pungetc();
11518 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011519 }
11520#endif
11521 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011522 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011523 default: {
11524 static const char types[] ALIGN1 = "}-+?=";
11525 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011526 if (p == NULL)
11527 goto badsub;
11528 subtype = p - types + VSNORMAL;
11529 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011530 }
Eric Andersenc470f442003-07-28 09:56:35 +000011531 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011532 case '#': {
11533 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011534 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011535 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011536 if (c != cc)
11537 goto do_pungetc;
11538 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011539 break;
11540 }
11541#if ENABLE_ASH_BASH_COMPAT
11542 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011543 /* ${v/[/]pattern/repl} */
11544//TODO: encode pattern and repl separately.
11545// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011546 subtype = VSREPLACE;
11547 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011548 if (c != '/')
11549 goto do_pungetc;
11550 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011551 break;
11552#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011553 }
Eric Andersenc470f442003-07-28 09:56:35 +000011554 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011555 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011556 pungetc();
11557 }
11558 if (dblquote || arinest)
11559 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011560 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011561 if (subtype != VSNORMAL) {
11562 varnest++;
11563 if (dblquote || arinest) {
11564 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011565 }
11566 }
11567 }
Eric Andersenc470f442003-07-28 09:56:35 +000011568 goto parsesub_return;
11569}
Eric Andersencb57d552001-06-28 07:25:16 +000011570
Eric Andersencb57d552001-06-28 07:25:16 +000011571/*
11572 * Called to parse command substitutions. Newstyle is set if the command
11573 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11574 * list of commands (passed by reference), and savelen is the number of
11575 * characters on the top of the stack which must be preserved.
11576 */
Eric Andersenc470f442003-07-28 09:56:35 +000011577parsebackq: {
11578 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011579 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011580 union node *n;
11581 char *volatile str;
11582 struct jmploc jmploc;
11583 struct jmploc *volatile savehandler;
11584 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011585 smallint saveprompt = 0;
11586
Eric Andersencb57d552001-06-28 07:25:16 +000011587#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011588 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011589#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011590 savepbq = parsebackquote;
11591 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011592 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011593 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011594 exception_handler = savehandler;
11595 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011596 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011597 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011598 str = NULL;
11599 savelen = out - (char *)stackblock();
11600 if (savelen > 0) {
11601 str = ckmalloc(savelen);
11602 memcpy(str, stackblock(), savelen);
11603 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011604 savehandler = exception_handler;
11605 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011606 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011607 if (oldstyle) {
11608 /* We must read until the closing backquote, giving special
11609 treatment to some slashes, and then push the string and
11610 reread it as input, interpreting it normally. */
11611 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011612 size_t psavelen;
11613 char *pstr;
11614
Eric Andersenc470f442003-07-28 09:56:35 +000011615 STARTSTACKSTR(pout);
11616 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011617 int pc;
11618
11619 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011620 pc = pgetc();
11621 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011622 case '`':
11623 goto done;
11624
11625 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011626 pc = pgetc();
11627 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011628 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011629 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011630 /*
11631 * If eating a newline, avoid putting
11632 * the newline into the new character
11633 * stream (via the STPUTC after the
11634 * switch).
11635 */
11636 continue;
11637 }
11638 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011639 && (!dblquote || pc != '"')
11640 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011641 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011642 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011643 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011644 break;
11645 }
11646 /* fall through */
11647
11648 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011649 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011650 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011651 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011652
11653 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011654 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011655 needprompt = doprompt;
11656 break;
11657
11658 default:
11659 break;
11660 }
11661 STPUTC(pc, pout);
11662 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011663 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011664 STPUTC('\0', pout);
11665 psavelen = pout - (char *)stackblock();
11666 if (psavelen > 0) {
11667 pstr = grabstackstr(pout);
11668 setinputstring(pstr);
11669 }
11670 }
11671 nlpp = &bqlist;
11672 while (*nlpp)
11673 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011674 *nlpp = stzalloc(sizeof(**nlpp));
11675 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011676 parsebackquote = oldstyle;
11677
11678 if (oldstyle) {
11679 saveprompt = doprompt;
11680 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011681 }
11682
Eric Andersenc470f442003-07-28 09:56:35 +000011683 n = list(2);
11684
11685 if (oldstyle)
11686 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011687 else if (readtoken() != TRP)
11688 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011689
11690 (*nlpp)->n = n;
11691 if (oldstyle) {
11692 /*
11693 * Start reading from old file again, ignoring any pushed back
11694 * tokens left from the backquote parsing
11695 */
11696 popfile();
11697 tokpushback = 0;
11698 }
11699 while (stackblocksize() <= savelen)
11700 growstackblock();
11701 STARTSTACKSTR(out);
11702 if (str) {
11703 memcpy(out, str, savelen);
11704 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011705 INT_OFF;
11706 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011707 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011708 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011709 }
11710 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011711 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011712 if (arinest || dblquote)
11713 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11714 else
11715 USTPUTC(CTLBACKQ, out);
11716 if (oldstyle)
11717 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011718 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011719}
11720
Mike Frysinger98c52642009-04-02 10:02:37 +000011721#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011722/*
11723 * Parse an arithmetic expansion (indicate start of one and set state)
11724 */
Eric Andersenc470f442003-07-28 09:56:35 +000011725parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011726 if (++arinest == 1) {
11727 prevsyntax = syntax;
11728 syntax = ARISYNTAX;
11729 USTPUTC(CTLARI, out);
11730 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011731 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011732 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011733 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011734 } else {
11735 /*
11736 * we collapse embedded arithmetic expansion to
11737 * parenthesis, which should be equivalent
11738 */
11739 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011740 }
Eric Andersenc470f442003-07-28 09:56:35 +000011741 goto parsearith_return;
11742}
11743#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011744
Eric Andersenc470f442003-07-28 09:56:35 +000011745} /* end of readtoken */
11746
Eric Andersencb57d552001-06-28 07:25:16 +000011747/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011748 * Read the next input token.
11749 * If the token is a word, we set backquotelist to the list of cmds in
11750 * backquotes. We set quoteflag to true if any part of the word was
11751 * quoted.
11752 * If the token is TREDIR, then we set redirnode to a structure containing
11753 * the redirection.
11754 * In all cases, the variable startlinno is set to the number of the line
11755 * on which the token starts.
11756 *
11757 * [Change comment: here documents and internal procedures]
11758 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11759 * word parsing code into a separate routine. In this case, readtoken
11760 * doesn't need to have any internal procedures, but parseword does.
11761 * We could also make parseoperator in essence the main routine, and
11762 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011763 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011764#define NEW_xxreadtoken
11765#ifdef NEW_xxreadtoken
11766/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011767static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011768 '\n', '(', ')', /* singles */
11769 '&', '|', ';', /* doubles */
11770 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011771};
Eric Andersencb57d552001-06-28 07:25:16 +000011772
Denis Vlasenko834dee72008-10-07 09:18:30 +000011773#define xxreadtoken_singles 3
11774#define xxreadtoken_doubles 3
11775
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011776static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011777 TNL, TLP, TRP, /* only single occurrence allowed */
11778 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11779 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011780 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011781};
11782
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011783static int
11784xxreadtoken(void)
11785{
11786 int c;
11787
11788 if (tokpushback) {
11789 tokpushback = 0;
11790 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011791 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011792 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011793 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011794 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011795 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011796 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011797 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011798
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011799 if (c == '#') {
11800 while ((c = pgetc()) != '\n' && c != PEOF)
11801 continue;
11802 pungetc();
11803 } else if (c == '\\') {
11804 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011805 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011806 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011807 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011808 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011809 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011810 } else {
11811 const char *p;
11812
11813 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11814 if (c != PEOF) {
11815 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011816 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011817 needprompt = doprompt;
11818 }
11819
11820 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011821 if (p == NULL)
11822 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011823
Denis Vlasenko834dee72008-10-07 09:18:30 +000011824 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11825 int cc = pgetc();
11826 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011827 p += xxreadtoken_doubles + 1;
11828 } else {
11829 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011830#if ENABLE_ASH_BASH_COMPAT
11831 if (c == '&' && cc == '>') /* &> */
11832 break; /* return readtoken1(...) */
11833#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011834 }
11835 }
11836 }
11837 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11838 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011839 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011840 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011841
11842 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011843}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011844#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011845#define RETURN(token) return lasttoken = token
11846static int
11847xxreadtoken(void)
11848{
11849 int c;
11850
11851 if (tokpushback) {
11852 tokpushback = 0;
11853 return lasttoken;
11854 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011855 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011856 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011857 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011858 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011859 switch (c) {
11860 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011861 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011862 continue;
11863 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011864 while ((c = pgetc()) != '\n' && c != PEOF)
11865 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011866 pungetc();
11867 continue;
11868 case '\\':
11869 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011870 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011871 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011872 continue;
11873 }
11874 pungetc();
11875 goto breakloop;
11876 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011877 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011878 needprompt = doprompt;
11879 RETURN(TNL);
11880 case PEOF:
11881 RETURN(TEOF);
11882 case '&':
11883 if (pgetc() == '&')
11884 RETURN(TAND);
11885 pungetc();
11886 RETURN(TBACKGND);
11887 case '|':
11888 if (pgetc() == '|')
11889 RETURN(TOR);
11890 pungetc();
11891 RETURN(TPIPE);
11892 case ';':
11893 if (pgetc() == ';')
11894 RETURN(TENDCASE);
11895 pungetc();
11896 RETURN(TSEMI);
11897 case '(':
11898 RETURN(TLP);
11899 case ')':
11900 RETURN(TRP);
11901 default:
11902 goto breakloop;
11903 }
11904 }
11905 breakloop:
11906 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11907#undef RETURN
11908}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011909#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011910
11911static int
11912readtoken(void)
11913{
11914 int t;
11915#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011916 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011917#endif
11918
11919#if ENABLE_ASH_ALIAS
11920 top:
11921#endif
11922
11923 t = xxreadtoken();
11924
11925 /*
11926 * eat newlines
11927 */
11928 if (checkkwd & CHKNL) {
11929 while (t == TNL) {
11930 parseheredoc();
11931 t = xxreadtoken();
11932 }
11933 }
11934
11935 if (t != TWORD || quoteflag) {
11936 goto out;
11937 }
11938
11939 /*
11940 * check for keywords
11941 */
11942 if (checkkwd & CHKKWD) {
11943 const char *const *pp;
11944
11945 pp = findkwd(wordtext);
11946 if (pp) {
11947 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011948 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011949 goto out;
11950 }
11951 }
11952
11953 if (checkkwd & CHKALIAS) {
11954#if ENABLE_ASH_ALIAS
11955 struct alias *ap;
11956 ap = lookupalias(wordtext, 1);
11957 if (ap != NULL) {
11958 if (*ap->val) {
11959 pushstring(ap->val, ap);
11960 }
11961 goto top;
11962 }
11963#endif
11964 }
11965 out:
11966 checkkwd = 0;
11967#if DEBUG
11968 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011969 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011970 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011971 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011972#endif
11973 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011974}
11975
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011976static char
11977peektoken(void)
11978{
11979 int t;
11980
11981 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011982 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011983 return tokname_array[t][0];
11984}
Eric Andersencb57d552001-06-28 07:25:16 +000011985
11986/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011987 * Read and parse a command. Returns NODE_EOF on end of file.
11988 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011989 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011990static union node *
11991parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011992{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011993 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011994
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011995 tokpushback = 0;
11996 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011997 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011998 needprompt = 0;
11999 t = readtoken();
12000 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012001 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012002 if (t == TNL)
12003 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012004 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012005 return list(1);
12006}
12007
12008/*
12009 * Input any here documents.
12010 */
12011static void
12012parseheredoc(void)
12013{
12014 struct heredoc *here;
12015 union node *n;
12016
12017 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012018 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012019
12020 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012021 setprompt_if(needprompt, 2);
12022 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012023 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012024 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012025 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012026 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012027 n->narg.text = wordtext;
12028 n->narg.backquote = backquotelist;
12029 here->here->nhere.doc = n;
12030 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012031 }
Eric Andersencb57d552001-06-28 07:25:16 +000012032}
12033
12034
12035/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012036 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012037 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012038#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012039static const char *
12040expandstr(const char *ps)
12041{
12042 union node n;
12043
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012044 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12045 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012046 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012047 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012048 popfile();
12049
12050 n.narg.type = NARG;
12051 n.narg.next = NULL;
12052 n.narg.text = wordtext;
12053 n.narg.backquote = backquotelist;
12054
12055 expandarg(&n, NULL, 0);
12056 return stackblock();
12057}
12058#endif
12059
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012060/*
12061 * Execute a command or commands contained in a string.
12062 */
12063static int
12064evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012065{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012066 union node *n;
12067 struct stackmark smark;
12068 int skip;
12069
12070 setinputstring(s);
12071 setstackmark(&smark);
12072
12073 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012074 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012075 evaltree(n, 0);
12076 popstackmark(&smark);
12077 skip = evalskip;
12078 if (skip)
12079 break;
12080 }
12081 popfile();
12082
12083 skip &= mask;
12084 evalskip = skip;
12085 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012086}
12087
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012088/*
12089 * The eval command.
12090 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012091static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012092evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012093{
12094 char *p;
12095 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012096
Denis Vlasenko68404f12008-03-17 09:00:54 +000012097 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012098 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012099 argv += 2;
12100 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012101 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012102 for (;;) {
12103 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012104 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012105 if (p == NULL)
12106 break;
12107 STPUTC(' ', concat);
12108 }
12109 STPUTC('\0', concat);
12110 p = grabstackstr(concat);
12111 }
12112 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012113 }
12114 return exitstatus;
12115}
12116
12117/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012118 * Read and execute commands.
12119 * "Top" is nonzero for the top level command loop;
12120 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012121 */
12122static int
12123cmdloop(int top)
12124{
12125 union node *n;
12126 struct stackmark smark;
12127 int inter;
12128 int numeof = 0;
12129
12130 TRACE(("cmdloop(%d) called\n", top));
12131 for (;;) {
12132 int skip;
12133
12134 setstackmark(&smark);
12135#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012136 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012137 showjobs(stderr, SHOW_CHANGED);
12138#endif
12139 inter = 0;
12140 if (iflag && top) {
12141 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012142 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012143 }
12144 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012145#if DEBUG
12146 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012147 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012148#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012149 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012150 if (!top || numeof >= 50)
12151 break;
12152 if (!stoppedjobs()) {
12153 if (!Iflag)
12154 break;
12155 out2str("\nUse \"exit\" to leave shell.\n");
12156 }
12157 numeof++;
12158 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012159 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12160 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012161 numeof = 0;
12162 evaltree(n, 0);
12163 }
12164 popstackmark(&smark);
12165 skip = evalskip;
12166
12167 if (skip) {
12168 evalskip = 0;
12169 return skip & SKIPEVAL;
12170 }
12171 }
12172 return 0;
12173}
12174
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012175/*
12176 * Take commands from a file. To be compatible we should do a path
12177 * search for the file, which is necessary to find sub-commands.
12178 */
12179static char *
12180find_dot_file(char *name)
12181{
12182 char *fullname;
12183 const char *path = pathval();
12184 struct stat statb;
12185
12186 /* don't try this for absolute or relative paths */
12187 if (strchr(name, '/'))
12188 return name;
12189
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012190 /* IIRC standards do not say whether . is to be searched.
12191 * And it is even smaller this way, making it unconditional for now:
12192 */
12193 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12194 fullname = name;
12195 goto try_cur_dir;
12196 }
12197
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012198 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012199 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012200 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12201 /*
12202 * Don't bother freeing here, since it will
12203 * be freed by the caller.
12204 */
12205 return fullname;
12206 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012207 if (fullname != name)
12208 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012209 }
12210
12211 /* not found in the PATH */
12212 ash_msg_and_raise_error("%s: not found", name);
12213 /* NOTREACHED */
12214}
12215
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012216static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012217dotcmd(int argc, char **argv)
12218{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012219 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012220 struct strlist *sp;
12221 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012222
12223 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012224 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012225
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012226 if (!argv[1]) {
12227 /* bash says: "bash: .: filename argument required" */
12228 return 2; /* bash compat */
12229 }
12230
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012231 /* "false; . empty_file; echo $?" should print 0, not 1: */
12232 exitstatus = 0;
12233
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012234 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012235
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012236 argv += 2;
12237 argc -= 2;
12238 if (argc) { /* argc > 0, argv[0] != NULL */
12239 saveparam = shellparam;
12240 shellparam.malloced = 0;
12241 shellparam.nparam = argc;
12242 shellparam.p = argv;
12243 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012244
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012245 setinputfile(fullname, INPUT_PUSH_FILE);
12246 commandname = fullname;
12247 cmdloop(0);
12248 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012249
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012250 if (argc) {
12251 freeparam(&shellparam);
12252 shellparam = saveparam;
12253 };
12254
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012255 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012256}
12257
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012258static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012259exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012260{
12261 if (stoppedjobs())
12262 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012263 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012264 exitstatus = number(argv[1]);
12265 raise_exception(EXEXIT);
12266 /* NOTREACHED */
12267}
12268
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012269/*
12270 * Read a file containing shell functions.
12271 */
12272static void
12273readcmdfile(char *name)
12274{
12275 setinputfile(name, INPUT_PUSH_FILE);
12276 cmdloop(0);
12277 popfile();
12278}
12279
12280
Denis Vlasenkocc571512007-02-23 21:10:35 +000012281/* ============ find_command inplementation */
12282
12283/*
12284 * Resolve a command name. If you change this routine, you may have to
12285 * change the shellexec routine as well.
12286 */
12287static void
12288find_command(char *name, struct cmdentry *entry, int act, const char *path)
12289{
12290 struct tblentry *cmdp;
12291 int idx;
12292 int prev;
12293 char *fullname;
12294 struct stat statb;
12295 int e;
12296 int updatetbl;
12297 struct builtincmd *bcmd;
12298
12299 /* If name contains a slash, don't use PATH or hash table */
12300 if (strchr(name, '/') != NULL) {
12301 entry->u.index = -1;
12302 if (act & DO_ABS) {
12303 while (stat(name, &statb) < 0) {
12304#ifdef SYSV
12305 if (errno == EINTR)
12306 continue;
12307#endif
12308 entry->cmdtype = CMDUNKNOWN;
12309 return;
12310 }
12311 }
12312 entry->cmdtype = CMDNORMAL;
12313 return;
12314 }
12315
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012316/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012317
12318 updatetbl = (path == pathval());
12319 if (!updatetbl) {
12320 act |= DO_ALTPATH;
12321 if (strstr(path, "%builtin") != NULL)
12322 act |= DO_ALTBLTIN;
12323 }
12324
12325 /* If name is in the table, check answer will be ok */
12326 cmdp = cmdlookup(name, 0);
12327 if (cmdp != NULL) {
12328 int bit;
12329
12330 switch (cmdp->cmdtype) {
12331 default:
12332#if DEBUG
12333 abort();
12334#endif
12335 case CMDNORMAL:
12336 bit = DO_ALTPATH;
12337 break;
12338 case CMDFUNCTION:
12339 bit = DO_NOFUNC;
12340 break;
12341 case CMDBUILTIN:
12342 bit = DO_ALTBLTIN;
12343 break;
12344 }
12345 if (act & bit) {
12346 updatetbl = 0;
12347 cmdp = NULL;
12348 } else if (cmdp->rehash == 0)
12349 /* if not invalidated by cd, we're done */
12350 goto success;
12351 }
12352
12353 /* If %builtin not in path, check for builtin next */
12354 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012355 if (bcmd) {
12356 if (IS_BUILTIN_REGULAR(bcmd))
12357 goto builtin_success;
12358 if (act & DO_ALTPATH) {
12359 if (!(act & DO_ALTBLTIN))
12360 goto builtin_success;
12361 } else if (builtinloc <= 0) {
12362 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012363 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012364 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012365
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012366#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012367 {
12368 int applet_no = find_applet_by_name(name);
12369 if (applet_no >= 0) {
12370 entry->cmdtype = CMDNORMAL;
12371 entry->u.index = -2 - applet_no;
12372 return;
12373 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012374 }
12375#endif
12376
Denis Vlasenkocc571512007-02-23 21:10:35 +000012377 /* We have to search path. */
12378 prev = -1; /* where to start */
12379 if (cmdp && cmdp->rehash) { /* doing a rehash */
12380 if (cmdp->cmdtype == CMDBUILTIN)
12381 prev = builtinloc;
12382 else
12383 prev = cmdp->param.index;
12384 }
12385
12386 e = ENOENT;
12387 idx = -1;
12388 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012389 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012390 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012391 /* NB: code below will still use fullname
12392 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012393 idx++;
12394 if (pathopt) {
12395 if (prefix(pathopt, "builtin")) {
12396 if (bcmd)
12397 goto builtin_success;
12398 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012399 }
12400 if ((act & DO_NOFUNC)
12401 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012402 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012403 continue;
12404 }
12405 }
12406 /* if rehash, don't redo absolute path names */
12407 if (fullname[0] == '/' && idx <= prev) {
12408 if (idx < prev)
12409 continue;
12410 TRACE(("searchexec \"%s\": no change\n", name));
12411 goto success;
12412 }
12413 while (stat(fullname, &statb) < 0) {
12414#ifdef SYSV
12415 if (errno == EINTR)
12416 continue;
12417#endif
12418 if (errno != ENOENT && errno != ENOTDIR)
12419 e = errno;
12420 goto loop;
12421 }
12422 e = EACCES; /* if we fail, this will be the error */
12423 if (!S_ISREG(statb.st_mode))
12424 continue;
12425 if (pathopt) { /* this is a %func directory */
12426 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012427 /* NB: stalloc will return space pointed by fullname
12428 * (because we don't have any intervening allocations
12429 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012430 readcmdfile(fullname);
12431 cmdp = cmdlookup(name, 0);
12432 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12433 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12434 stunalloc(fullname);
12435 goto success;
12436 }
12437 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12438 if (!updatetbl) {
12439 entry->cmdtype = CMDNORMAL;
12440 entry->u.index = idx;
12441 return;
12442 }
12443 INT_OFF;
12444 cmdp = cmdlookup(name, 1);
12445 cmdp->cmdtype = CMDNORMAL;
12446 cmdp->param.index = idx;
12447 INT_ON;
12448 goto success;
12449 }
12450
12451 /* We failed. If there was an entry for this command, delete it */
12452 if (cmdp && updatetbl)
12453 delete_cmd_entry();
12454 if (act & DO_ERR)
12455 ash_msg("%s: %s", name, errmsg(e, "not found"));
12456 entry->cmdtype = CMDUNKNOWN;
12457 return;
12458
12459 builtin_success:
12460 if (!updatetbl) {
12461 entry->cmdtype = CMDBUILTIN;
12462 entry->u.cmd = bcmd;
12463 return;
12464 }
12465 INT_OFF;
12466 cmdp = cmdlookup(name, 1);
12467 cmdp->cmdtype = CMDBUILTIN;
12468 cmdp->param.cmd = bcmd;
12469 INT_ON;
12470 success:
12471 cmdp->rehash = 0;
12472 entry->cmdtype = cmdp->cmdtype;
12473 entry->u = cmdp->param;
12474}
12475
12476
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012477/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012478
Eric Andersencb57d552001-06-28 07:25:16 +000012479/*
Eric Andersencb57d552001-06-28 07:25:16 +000012480 * The trap builtin.
12481 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012482static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012483trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012484{
12485 char *action;
12486 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012487 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012488
Eric Andersenc470f442003-07-28 09:56:35 +000012489 nextopt(nullstr);
12490 ap = argptr;
12491 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012492 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012493 char *tr = trap_ptr[signo];
12494 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012495 /* note: bash adds "SIG", but only if invoked
12496 * as "bash". If called as "sh", or if set -o posix,
12497 * then it prints short signal names.
12498 * We are printing short names: */
12499 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012500 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012501 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012502 /* trap_ptr != trap only if we are in special-cased `trap` code.
12503 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012504 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012505 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012506 }
12507 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012508 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012509 if (trap_ptr != trap) {
12510 free(trap_ptr);
12511 trap_ptr = trap;
12512 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012513 */
Eric Andersencb57d552001-06-28 07:25:16 +000012514 return 0;
12515 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012516
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012517 action = NULL;
12518 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012519 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012520 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012521 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012522 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012523 if (signo < 0) {
12524 /* Mimic bash message exactly */
12525 ash_msg("%s: invalid signal specification", *ap);
12526 exitcode = 1;
12527 goto next;
12528 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012529 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012530 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012531 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012532 action = NULL;
12533 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012534 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012535 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012536 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012537 if (action)
12538 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012539 trap[signo] = action;
12540 if (signo != 0)
12541 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012542 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012543 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012544 ap++;
12545 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012546 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012547}
12548
Eric Andersenc470f442003-07-28 09:56:35 +000012549
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012550/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012551
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012552#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012553/*
12554 * Lists available builtins
12555 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012556static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012557helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012558{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012559 unsigned col;
12560 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012561
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012562 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012563 "Built-in commands:\n"
12564 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012565 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012566 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012567 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012568 if (col > 60) {
12569 out1fmt("\n");
12570 col = 0;
12571 }
12572 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012573#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012574 {
12575 const char *a = applet_names;
12576 while (*a) {
12577 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12578 if (col > 60) {
12579 out1fmt("\n");
12580 col = 0;
12581 }
12582 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012583 }
12584 }
12585#endif
12586 out1fmt("\n\n");
12587 return EXIT_SUCCESS;
12588}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012589#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012590
Eric Andersencb57d552001-06-28 07:25:16 +000012591/*
Eric Andersencb57d552001-06-28 07:25:16 +000012592 * The export and readonly commands.
12593 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012594static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012595exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012596{
12597 struct var *vp;
12598 char *name;
12599 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012600 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012601 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012602
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012603 if (nextopt("p") != 'p') {
12604 aptr = argptr;
12605 name = *aptr;
12606 if (name) {
12607 do {
12608 p = strchr(name, '=');
12609 if (p != NULL) {
12610 p++;
12611 } else {
12612 vp = *findvar(hashvar(name), name);
12613 if (vp) {
12614 vp->flags |= flag;
12615 continue;
12616 }
Eric Andersencb57d552001-06-28 07:25:16 +000012617 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012618 setvar(name, p, flag);
12619 } while ((name = *++aptr) != NULL);
12620 return 0;
12621 }
Eric Andersencb57d552001-06-28 07:25:16 +000012622 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012623 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012624 return 0;
12625}
12626
Eric Andersencb57d552001-06-28 07:25:16 +000012627/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012628 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012629 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012630static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012631unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012632{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012633 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012634
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012635 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012636 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012637 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012638}
12639
Eric Andersencb57d552001-06-28 07:25:16 +000012640/*
Eric Andersencb57d552001-06-28 07:25:16 +000012641 * The unset builtin command. We unset the function before we unset the
12642 * variable to allow a function to be unset when there is a readonly variable
12643 * with the same name.
12644 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012645static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012646unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012647{
12648 char **ap;
12649 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012650 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012651 int ret = 0;
12652
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012653 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012654 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012655 }
Eric Andersencb57d552001-06-28 07:25:16 +000012656
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012657 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012658 if (flag != 'f') {
12659 i = unsetvar(*ap);
12660 ret |= i;
12661 if (!(i & 2))
12662 continue;
12663 }
12664 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012665 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012666 }
Eric Andersenc470f442003-07-28 09:56:35 +000012667 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012668}
12669
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012670static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012671 ' ', offsetof(struct tms, tms_utime),
12672 '\n', offsetof(struct tms, tms_stime),
12673 ' ', offsetof(struct tms, tms_cutime),
12674 '\n', offsetof(struct tms, tms_cstime),
12675 0
12676};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012677static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012678timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012679{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012680 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012681 const unsigned char *p;
12682 struct tms buf;
12683
12684 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012685 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012686
12687 p = timescmd_str;
12688 do {
12689 t = *(clock_t *)(((char *) &buf) + p[1]);
12690 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012691 t = t % clk_tck;
12692 out1fmt("%lum%lu.%03lus%c",
12693 s / 60, s % 60,
12694 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012695 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012696 p += 2;
12697 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012698
Eric Andersencb57d552001-06-28 07:25:16 +000012699 return 0;
12700}
12701
Mike Frysinger98c52642009-04-02 10:02:37 +000012702#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012703/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012704 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012705 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012706 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012707 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012708 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012709static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012710letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012711{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012712 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012713
Denis Vlasenko68404f12008-03-17 09:00:54 +000012714 argv++;
12715 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012716 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012717 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012718 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012719 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012720
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012721 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012722}
Eric Andersenc470f442003-07-28 09:56:35 +000012723#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012724
Eric Andersenc470f442003-07-28 09:56:35 +000012725/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012726 * The read builtin. Options:
12727 * -r Do not interpret '\' specially
12728 * -s Turn off echo (tty only)
12729 * -n NCHARS Read NCHARS max
12730 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12731 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12732 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012733 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012734 * TODO: bash also has:
12735 * -a ARRAY Read into array[0],[1],etc
12736 * -d DELIM End on DELIM char, not newline
12737 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012738 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012739static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012740readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012741{
Denys Vlasenko73067272010-01-12 22:11:24 +010012742 char *opt_n = NULL;
12743 char *opt_p = NULL;
12744 char *opt_t = NULL;
12745 char *opt_u = NULL;
12746 int read_flags = 0;
12747 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012748 int i;
12749
Denys Vlasenko73067272010-01-12 22:11:24 +010012750 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012751 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012752 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012753 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012754 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012755 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012756 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012757 break;
12758 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012759 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012760 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012761 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012762 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012763 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012764 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012765 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012766 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012767 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012768 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012769 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012770 default:
12771 break;
12772 }
Eric Andersenc470f442003-07-28 09:56:35 +000012773 }
Paul Fox02eb9342005-09-07 16:56:02 +000012774
Denys Vlasenko03dad222010-01-12 23:29:57 +010012775 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012776 argptr,
12777 bltinlookup("IFS"), /* can be NULL */
12778 read_flags,
12779 opt_n,
12780 opt_p,
12781 opt_t,
12782 opt_u
12783 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012784
Denys Vlasenko73067272010-01-12 22:11:24 +010012785 if ((uintptr_t)r > 1)
12786 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012787
Denys Vlasenko73067272010-01-12 22:11:24 +010012788 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012789}
12790
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012791static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012792umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012793{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012794 static const char permuser[3] ALIGN1 = "ugo";
12795 static const char permmode[3] ALIGN1 = "rwx";
12796 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012797 S_IRUSR, S_IWUSR, S_IXUSR,
12798 S_IRGRP, S_IWGRP, S_IXGRP,
12799 S_IROTH, S_IWOTH, S_IXOTH
12800 };
12801
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012802 /* TODO: use bb_parse_mode() instead */
12803
Eric Andersenc470f442003-07-28 09:56:35 +000012804 char *ap;
12805 mode_t mask;
12806 int i;
12807 int symbolic_mode = 0;
12808
12809 while (nextopt("S") != '\0') {
12810 symbolic_mode = 1;
12811 }
12812
Denis Vlasenkob012b102007-02-19 22:43:01 +000012813 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012814 mask = umask(0);
12815 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012816 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012817
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012818 ap = *argptr;
12819 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012820 if (symbolic_mode) {
12821 char buf[18];
12822 char *p = buf;
12823
12824 for (i = 0; i < 3; i++) {
12825 int j;
12826
12827 *p++ = permuser[i];
12828 *p++ = '=';
12829 for (j = 0; j < 3; j++) {
12830 if ((mask & permmask[3 * i + j]) == 0) {
12831 *p++ = permmode[j];
12832 }
12833 }
12834 *p++ = ',';
12835 }
12836 *--p = 0;
12837 puts(buf);
12838 } else {
12839 out1fmt("%.4o\n", mask);
12840 }
12841 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012842 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012843 mask = 0;
12844 do {
12845 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012846 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012847 mask = (mask << 3) + (*ap - '0');
12848 } while (*++ap != '\0');
12849 umask(mask);
12850 } else {
12851 mask = ~mask & 0777;
12852 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012853 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012854 }
12855 umask(~mask & 0777);
12856 }
12857 }
12858 return 0;
12859}
12860
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012861static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012862ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012863{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012864 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012865}
12866
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012867/* ============ main() and helpers */
12868
12869/*
12870 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012871 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012872static void
12873exitshell(void)
12874{
12875 struct jmploc loc;
12876 char *p;
12877 int status;
12878
12879 status = exitstatus;
12880 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12881 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012882 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012883/* dash bug: it just does _exit(exitstatus) here
12884 * but we have to do setjobctl(0) first!
12885 * (bug is still not fixed in dash-0.5.3 - if you run dash
12886 * under Midnight Commander, on exit from dash MC is backgrounded) */
12887 status = exitstatus;
12888 goto out;
12889 }
12890 exception_handler = &loc;
12891 p = trap[0];
12892 if (p) {
12893 trap[0] = NULL;
12894 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012895 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012896 }
12897 flush_stdout_stderr();
12898 out:
12899 setjobctl(0);
12900 _exit(status);
12901 /* NOTREACHED */
12902}
12903
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012904static void
12905init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012906{
12907 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012908 /* we will never free this */
12909 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012910
12911 /* from trap.c: */
12912 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012913 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12914 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12915 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012916 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012917
12918 /* from var.c: */
12919 {
12920 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012921 const char *p;
12922 struct stat st1, st2;
12923
12924 initvar();
12925 for (envp = environ; envp && *envp; envp++) {
12926 if (strchr(*envp, '=')) {
12927 setvareq(*envp, VEXPORT|VTEXTFIXED);
12928 }
12929 }
12930
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012931 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012932
12933 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012934 if (p) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012935 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012936 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
12937 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012938 p = '\0';
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012939 }
12940 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012941 setpwd(p, 0);
12942 }
12943}
12944
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012945
12946//usage:#define ash_trivial_usage
Denys Vlasenko6b6af532011-03-08 10:24:17 +010012947//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012948//usage:#define ash_full_usage "\n\n"
12949//usage: "Unix shell interpreter"
12950
12951//usage:#if ENABLE_FEATURE_SH_IS_ASH
12952//usage:# define sh_trivial_usage ash_trivial_usage
12953//usage:# define sh_full_usage ash_full_usage
12954//usage:#endif
12955//usage:#if ENABLE_FEATURE_BASH_IS_ASH
12956//usage:# define bash_trivial_usage ash_trivial_usage
12957//usage:# define bash_full_usage ash_full_usage
12958//usage:#endif
12959
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012960/*
12961 * Process the shell command line arguments.
12962 */
12963static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012964procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012965{
12966 int i;
12967 const char *xminusc;
12968 char **xargv;
12969
12970 xargv = argv;
12971 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012972 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012973 xargv++;
12974 for (i = 0; i < NOPTS; i++)
12975 optlist[i] = 2;
12976 argptr = xargv;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012977 if (options(/*cmdline:*/ 1)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012978 /* it already printed err message */
12979 raise_exception(EXERROR);
12980 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012981 xargv = argptr;
12982 xminusc = minusc;
12983 if (*xargv == NULL) {
12984 if (xminusc)
12985 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12986 sflag = 1;
12987 }
12988 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12989 iflag = 1;
12990 if (mflag == 2)
12991 mflag = iflag;
12992 for (i = 0; i < NOPTS; i++)
12993 if (optlist[i] == 2)
12994 optlist[i] = 0;
12995#if DEBUG == 2
12996 debug = 1;
12997#endif
12998 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12999 if (xminusc) {
13000 minusc = *xargv++;
13001 if (*xargv)
13002 goto setarg0;
13003 } else if (!sflag) {
13004 setinputfile(*xargv, 0);
13005 setarg0:
13006 arg0 = *xargv++;
13007 commandname = arg0;
13008 }
13009
13010 shellparam.p = xargv;
13011#if ENABLE_ASH_GETOPTS
13012 shellparam.optind = 1;
13013 shellparam.optoff = -1;
13014#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013015 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013016 while (*xargv) {
13017 shellparam.nparam++;
13018 xargv++;
13019 }
13020 optschanged();
13021}
13022
13023/*
13024 * Read /etc/profile or .profile.
13025 */
13026static void
13027read_profile(const char *name)
13028{
13029 int skip;
13030
13031 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13032 return;
13033 skip = cmdloop(0);
13034 popfile();
13035 if (skip)
13036 exitshell();
13037}
13038
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013039/*
13040 * This routine is called when an error or an interrupt occurs in an
13041 * interactive shell and control is returned to the main command loop.
13042 */
13043static void
13044reset(void)
13045{
13046 /* from eval.c: */
13047 evalskip = 0;
13048 loopnest = 0;
13049 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013050 g_parsefile->left_in_buffer = 0;
13051 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013052 popallfiles();
13053 /* from parser.c: */
13054 tokpushback = 0;
13055 checkkwd = 0;
13056 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013057 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013058}
13059
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013060#if PROFILE
13061static short profile_buf[16384];
13062extern int etext();
13063#endif
13064
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013065/*
13066 * Main routine. We initialize things, parse the arguments, execute
13067 * profiles if we're a login shell, and then call cmdloop to execute
13068 * commands. The setjmp call sets up the location to jump to when an
13069 * exception occurs. When an exception occurs the variable "state"
13070 * is used to figure out how far we had gotten.
13071 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013072int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013073int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013074{
Mike Frysinger98c52642009-04-02 10:02:37 +000013075 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013076 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013077 struct jmploc jmploc;
13078 struct stackmark smark;
13079
Denis Vlasenko01631112007-12-16 17:20:38 +000013080 /* Initialize global data */
13081 INIT_G_misc();
13082 INIT_G_memstack();
13083 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013084#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013085 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013086#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013087 INIT_G_cmdtable();
13088
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013089#if PROFILE
13090 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13091#endif
13092
13093#if ENABLE_FEATURE_EDITING
13094 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13095#endif
13096 state = 0;
13097 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013098 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013099 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013100
13101 reset();
13102
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013103 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013104 if (e == EXERROR)
13105 exitstatus = 2;
13106 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013107 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013108 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013109 }
13110 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013111 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013112 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013113
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013114 popstackmark(&smark);
13115 FORCE_INT_ON; /* enable interrupts */
13116 if (s == 1)
13117 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013118 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013119 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013120 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013121 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013122 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013123 }
13124 exception_handler = &jmploc;
13125#if DEBUG
13126 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013127 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013128 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013129#endif
13130 rootpid = getpid();
13131
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013132 init();
13133 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013134 procargs(argv);
13135
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013136#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13137 if (iflag) {
13138 const char *hp = lookupvar("HISTFILE");
13139
13140 if (hp == NULL) {
13141 hp = lookupvar("HOME");
13142 if (hp != NULL) {
13143 char *defhp = concat_path_file(hp, ".ash_history");
13144 setvar("HISTFILE", defhp, 0);
13145 free(defhp);
13146 }
13147 }
13148 }
13149#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013150 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013151 isloginsh = 1;
13152 if (isloginsh) {
13153 state = 1;
13154 read_profile("/etc/profile");
13155 state1:
13156 state = 2;
13157 read_profile(".profile");
13158 }
13159 state2:
13160 state = 3;
13161 if (
13162#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013163 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013164#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013165 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013166 ) {
13167 shinit = lookupvar("ENV");
13168 if (shinit != NULL && *shinit != '\0') {
13169 read_profile(shinit);
13170 }
13171 }
13172 state3:
13173 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013174 if (minusc) {
13175 /* evalstring pushes parsefile stack.
13176 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013177 * is one of stacked source fds.
13178 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013179 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013180 // ^^ not necessary since now we special-case fd 0
13181 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013182 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013183 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013184
13185 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013186#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013187 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013188 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013189 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013190 line_input_state->hist_file = hp;
13191 }
13192#endif
13193 state4: /* XXX ??? - why isn't this before the "if" statement */
13194 cmdloop(1);
13195 }
13196#if PROFILE
13197 monitor(0);
13198#endif
13199#ifdef GPROF
13200 {
13201 extern void _mcleanup(void);
13202 _mcleanup();
13203 }
13204#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013205 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013206 exitshell();
13207 /* NOTREACHED */
13208}
13209
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013210
Eric Andersendf82f612001-06-28 07:46:40 +000013211/*-
13212 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013213 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013214 *
13215 * This code is derived from software contributed to Berkeley by
13216 * Kenneth Almquist.
13217 *
13218 * Redistribution and use in source and binary forms, with or without
13219 * modification, are permitted provided that the following conditions
13220 * are met:
13221 * 1. Redistributions of source code must retain the above copyright
13222 * notice, this list of conditions and the following disclaimer.
13223 * 2. Redistributions in binary form must reproduce the above copyright
13224 * notice, this list of conditions and the following disclaimer in the
13225 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013226 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013227 * may be used to endorse or promote products derived from this software
13228 * without specific prior written permission.
13229 *
13230 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13231 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13232 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13233 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13234 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13235 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13236 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13237 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13238 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13239 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13240 * SUCH DAMAGE.
13241 */