blob: 1520c5ae5ed20a327ed8351fa09109dbcfb60bc5 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenkob012b102007-02-19 22:43:01 +000039#include <paths.h>
40#include <setjmp.h>
41#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020042#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010043
Denys Vlasenko20704f02011-03-23 17:59:27 +010044#include "busybox.h" /* for applet_names */
45#include "unicode.h"
46
Denys Vlasenko73067272010-01-12 22:11:24 +010047#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010048#if ENABLE_SH_MATH_SUPPORT
49# include "math.h"
50#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020051#if ENABLE_ASH_RANDOM_SUPPORT
52# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020053#else
54# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020055#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000056
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020057#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010058#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000059/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020060# undef CONFIG_FEATURE_SH_STANDALONE
61# undef ENABLE_FEATURE_SH_STANDALONE
62# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010063# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020064# define ENABLE_FEATURE_SH_STANDALONE 0
65# define IF_FEATURE_SH_STANDALONE(...)
66# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000067#endif
68
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000069#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000070# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000071#endif
72
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010073#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000074# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000075#endif
76
Denys Vlasenko771f1992010-07-16 14:31:34 +020077//config:config ASH
78//config: bool "ash"
79//config: default y
80//config: depends on !NOMMU
81//config: help
82//config: Tha 'ash' shell adds about 60k in the default configuration and is
83//config: the most complete and most pedantically correct shell included with
84//config: busybox. This shell is actually a derivative of the Debian 'dash'
85//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
86//config: (written by Kenneth Almquist) from NetBSD.
87//config:
88//config:config ASH_BASH_COMPAT
89//config: bool "bash-compatible extensions"
90//config: default y
91//config: depends on ASH
92//config: help
93//config: Enable bash-compatible extensions.
94//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +010095//config:config ASH_IDLE_TIMEOUT
96//config: bool "Idle timeout variable"
97//config: default n
98//config: depends on ASH
99//config: help
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100100//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko046341e2011-02-04 17:53:59 +0100101//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200102//config:config ASH_JOB_CONTROL
103//config: bool "Job control"
104//config: default y
105//config: depends on ASH
106//config: help
107//config: Enable job control in the ash shell.
108//config:
109//config:config ASH_ALIAS
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100110//config: bool "Alias support"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200111//config: default y
112//config: depends on ASH
113//config: help
114//config: Enable alias support in the ash shell.
115//config:
116//config:config ASH_GETOPTS
117//config: bool "Builtin getopt to parse positional parameters"
118//config: default y
119//config: depends on ASH
120//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100121//config: Enable support for getopts builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200122//config:
123//config:config ASH_BUILTIN_ECHO
124//config: bool "Builtin version of 'echo'"
125//config: default y
126//config: depends on ASH
127//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100128//config: Enable support for echo builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200129//config:
130//config:config ASH_BUILTIN_PRINTF
131//config: bool "Builtin version of 'printf'"
132//config: default y
133//config: depends on ASH
134//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100135//config: Enable support for printf builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200136//config:
137//config:config ASH_BUILTIN_TEST
138//config: bool "Builtin version of 'test'"
139//config: default y
140//config: depends on ASH
141//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100142//config: Enable support for test builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200143//config:
144//config:config ASH_CMDCMD
145//config: bool "'command' command to override shell builtins"
146//config: default y
147//config: depends on ASH
148//config: help
149//config: Enable support for the ash 'command' builtin, which allows
150//config: you to run the specified command with the specified arguments,
151//config: even when there is an ash builtin command with the same name.
152//config:
153//config:config ASH_MAIL
154//config: bool "Check for new mail on interactive shells"
155//config: default n
156//config: depends on ASH
157//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100158//config: Enable "check for new mail" function in the ash shell.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200159//config:
160//config:config ASH_OPTIMIZE_FOR_SIZE
161//config: bool "Optimize for size instead of speed"
162//config: default y
163//config: depends on ASH
164//config: help
165//config: Compile ash for reduced size at the price of speed.
166//config:
167//config:config ASH_RANDOM_SUPPORT
168//config: bool "Pseudorandom generator and $RANDOM variable"
169//config: default y
170//config: depends on ASH
171//config: help
172//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
173//config: Each read of "$RANDOM" will generate a new pseudorandom value.
174//config: You can reset the generator by using a specified start value.
175//config: After "unset RANDOM" the generator will switch off and this
176//config: variable will no longer have special treatment.
177//config:
178//config:config ASH_EXPAND_PRMT
179//config: bool "Expand prompt string"
180//config: default y
181//config: depends on ASH
182//config: help
183//config: "PS#" may contain volatile content, such as backquote commands.
184//config: This option recreates the prompt string from the environment
185//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200186//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200187
Denys Vlasenko20704f02011-03-23 17:59:27 +0100188//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
189//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
190//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
191
192//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
193//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
194
Denis Vlasenkob012b102007-02-19 22:43:01 +0000195
Denis Vlasenko01631112007-12-16 17:20:38 +0000196/* ============ Hash table sizes. Configurable. */
197
198#define VTABSIZE 39
199#define ATABSIZE 39
200#define CMDTABLESIZE 31 /* should be prime */
201
202
Denis Vlasenkob012b102007-02-19 22:43:01 +0000203/* ============ Shell options */
204
205static const char *const optletters_optnames[] = {
206 "e" "errexit",
207 "f" "noglob",
208 "I" "ignoreeof",
209 "i" "interactive",
210 "m" "monitor",
211 "n" "noexec",
212 "s" "stdin",
213 "x" "xtrace",
214 "v" "verbose",
215 "C" "noclobber",
216 "a" "allexport",
217 "b" "notify",
218 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100219 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100220#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100221 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100222#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000223#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000224 ,"\0" "nolog"
225 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000226#endif
227};
228
Denys Vlasenko285ad152009-12-04 23:02:27 +0100229#define optletters(n) optletters_optnames[n][0]
230#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000232enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000233
Eric Andersenc470f442003-07-28 09:56:35 +0000234
Denis Vlasenkob012b102007-02-19 22:43:01 +0000235/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000236
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200237#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000238
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000239/*
Eric Andersenc470f442003-07-28 09:56:35 +0000240 * We enclose jmp_buf in a structure so that we can declare pointers to
241 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000242 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000243 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000244 * exception handlers, the user should save the value of handler on entry
245 * to an inner scope, set handler to point to a jmploc structure for the
246 * inner scope, and restore handler on exit from the scope.
247 */
Eric Andersenc470f442003-07-28 09:56:35 +0000248struct jmploc {
249 jmp_buf loc;
250};
Denis Vlasenko01631112007-12-16 17:20:38 +0000251
252struct globals_misc {
253 /* pid of main shell */
254 int rootpid;
255 /* shell level: 0 for the main shell, 1 for its children, and so on */
256 int shlvl;
257#define rootshell (!shlvl)
258 char *minusc; /* argument to -c option */
259
260 char *curdir; // = nullstr; /* current working directory */
261 char *physdir; // = nullstr; /* physical working directory */
262
263 char *arg0; /* value of $0 */
264
265 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000266
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200267 volatile int suppress_int; /* counter */
268 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000269 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200270 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000271 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000272 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000273#define EXINT 0 /* SIGINT received */
274#define EXERROR 1 /* a generic error */
275#define EXSHELLPROC 2 /* execute a shell procedure */
276#define EXEXEC 3 /* command execution failed */
277#define EXEXIT 4 /* exit the shell */
278#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000279
Denis Vlasenko01631112007-12-16 17:20:38 +0000280 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000281 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000282
283 char optlist[NOPTS];
284#define eflag optlist[0]
285#define fflag optlist[1]
286#define Iflag optlist[2]
287#define iflag optlist[3]
288#define mflag optlist[4]
289#define nflag optlist[5]
290#define sflag optlist[6]
291#define xflag optlist[7]
292#define vflag optlist[8]
293#define Cflag optlist[9]
294#define aflag optlist[10]
295#define bflag optlist[11]
296#define uflag optlist[12]
297#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100298#if ENABLE_ASH_BASH_COMPAT
299# define pipefail optlist[14]
300#else
301# define pipefail 0
302#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000303#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100304# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
305# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000306#endif
307
308 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000309 /*
310 * Sigmode records the current value of the signal handlers for the various
311 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000312 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000313 */
314 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000315#define S_DFL 1 /* default signal handling (SIG_DFL) */
316#define S_CATCH 2 /* signal is caught */
317#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000318#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000319
Denis Vlasenko01631112007-12-16 17:20:38 +0000320 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000321 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200322 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000323 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200324 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000325
326 /* Rarely referenced stuff */
327#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200328 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000329#endif
330 pid_t backgndpid; /* pid of last background process */
331 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000332};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000333extern struct globals_misc *const ash_ptr_to_globals_misc;
334#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000335#define rootpid (G_misc.rootpid )
336#define shlvl (G_misc.shlvl )
337#define minusc (G_misc.minusc )
338#define curdir (G_misc.curdir )
339#define physdir (G_misc.physdir )
340#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000341#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000342#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200343#define suppress_int (G_misc.suppress_int )
344#define pending_int (G_misc.pending_int )
345#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000346#define isloginsh (G_misc.isloginsh )
347#define nullstr (G_misc.nullstr )
348#define optlist (G_misc.optlist )
349#define sigmode (G_misc.sigmode )
350#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200351#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000352#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200353#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200354#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000355#define backgndpid (G_misc.backgndpid )
356#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000357#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000358 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
359 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000360 curdir = nullstr; \
361 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200362 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000363} while (0)
364
365
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000366/* ============ DEBUG */
367#if DEBUG
368static void trace_printf(const char *fmt, ...);
369static void trace_vprintf(const char *fmt, va_list va);
370# define TRACE(param) trace_printf param
371# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000372# define close(fd) do { \
373 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000374 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200375 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000376 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000377} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000378#else
379# define TRACE(param)
380# define TRACEV(param)
381#endif
382
383
Denis Vlasenko559691a2008-10-05 18:39:31 +0000384/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000385#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
386
Denis Vlasenko559691a2008-10-05 18:39:31 +0000387static int isdigit_str9(const char *str)
388{
389 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
390 while (--maxlen && isdigit(*str))
391 str++;
392 return (*str == '\0');
393}
Denis Vlasenko01631112007-12-16 17:20:38 +0000394
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200395static const char *var_end(const char *var)
396{
397 while (*var)
398 if (*var++ == '=')
399 break;
400 return var;
401}
402
Denis Vlasenko559691a2008-10-05 18:39:31 +0000403
404/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100405
406static void exitshell(void) NORETURN;
407
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000408/*
Eric Andersen2870d962001-07-02 17:27:21 +0000409 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000410 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000411 * much more efficient and portable. (But hacking the kernel is so much
412 * more fun than worrying about efficiency and portability. :-))
413 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000414#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200415 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000416 xbarrier(); \
417} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000418
419/*
420 * Called to raise an exception. Since C doesn't include exceptions, we
421 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000422 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000423 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000424static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000425static void
426raise_exception(int e)
427{
428#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000429 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000430 abort();
431#endif
432 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000433 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000434 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000435}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000436#if DEBUG
437#define raise_exception(e) do { \
438 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
439 raise_exception(e); \
440} while (0)
441#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000442
443/*
444 * Called from trap.c when a SIGINT is received. (If the user specifies
445 * that SIGINT is to be trapped or ignored using the trap builtin, then
446 * this routine is not called.) Suppressint is nonzero when interrupts
447 * are held using the INT_OFF macro. (The test for iflag is just
448 * defensive programming.)
449 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000450static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000451static void
452raise_interrupt(void)
453{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000454 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000455
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200456 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000457 /* Signal is not automatically unmasked after it is raised,
458 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000459 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200460 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000461
Denis Vlasenko4b875702009-03-19 13:30:04 +0000462 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000463 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
464 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000465 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000466 signal(SIGINT, SIG_DFL);
467 raise(SIGINT);
468 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000469 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000470 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000471 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000472 /* NOTREACHED */
473}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000474#if DEBUG
475#define raise_interrupt() do { \
476 TRACE(("raising interrupt on line %d\n", __LINE__)); \
477 raise_interrupt(); \
478} while (0)
479#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000480
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000481static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000482int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000483{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000484 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200485 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000486 raise_interrupt();
487 }
488}
489#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000490static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000491force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000492{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000493 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200494 suppress_int = 0;
495 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000496 raise_interrupt();
497}
498#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000499
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200500#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000501
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000502#define RESTORE_INT(v) do { \
503 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200504 suppress_int = (v); \
505 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000506 raise_interrupt(); \
507} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000508
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000509
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000510/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000511
Eric Andersenc470f442003-07-28 09:56:35 +0000512static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000513outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000514{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000515 INT_OFF;
516 fputs(p, file);
517 INT_ON;
518}
519
520static void
521flush_stdout_stderr(void)
522{
523 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100524 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000525 INT_ON;
526}
527
528static void
529outcslow(int c, FILE *dest)
530{
531 INT_OFF;
532 putc(c, dest);
533 fflush(dest);
534 INT_ON;
535}
536
537static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
538static int
539out1fmt(const char *fmt, ...)
540{
541 va_list ap;
542 int r;
543
544 INT_OFF;
545 va_start(ap, fmt);
546 r = vprintf(fmt, ap);
547 va_end(ap);
548 INT_ON;
549 return r;
550}
551
552static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
553static int
554fmtstr(char *outbuf, size_t length, const char *fmt, ...)
555{
556 va_list ap;
557 int ret;
558
559 va_start(ap, fmt);
560 INT_OFF;
561 ret = vsnprintf(outbuf, length, fmt, ap);
562 va_end(ap);
563 INT_ON;
564 return ret;
565}
566
567static void
568out1str(const char *p)
569{
570 outstr(p, stdout);
571}
572
573static void
574out2str(const char *p)
575{
576 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100577 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000578}
579
580
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000581/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000582
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000583/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100584#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200585#define CTLESC ((unsigned char)'\201') /* escape next character */
586#define CTLVAR ((unsigned char)'\202') /* variable defn */
587#define CTLENDVAR ((unsigned char)'\203')
588#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000589#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
590/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200591#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
592#define CTLENDARI ((unsigned char)'\207')
593#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100594#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000595
596/* variable substitution byte (follows CTLVAR) */
597#define VSTYPE 0x0f /* type of variable substitution */
598#define VSNUL 0x10 /* colon--treat the empty string as unset */
599#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
600
601/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000602#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
603#define VSMINUS 0x2 /* ${var-text} */
604#define VSPLUS 0x3 /* ${var+text} */
605#define VSQUESTION 0x4 /* ${var?message} */
606#define VSASSIGN 0x5 /* ${var=text} */
607#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
608#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
609#define VSTRIMLEFT 0x8 /* ${var#pattern} */
610#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
611#define VSLENGTH 0xa /* ${#var} */
612#if ENABLE_ASH_BASH_COMPAT
613#define VSSUBSTR 0xc /* ${var:position:length} */
614#define VSREPLACE 0xd /* ${var/pattern/replacement} */
615#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
616#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000617
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000618static const char dolatstr[] ALIGN1 = {
619 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
620};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000621
Denis Vlasenko559691a2008-10-05 18:39:31 +0000622#define NCMD 0
623#define NPIPE 1
624#define NREDIR 2
625#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000626#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000627#define NAND 5
628#define NOR 6
629#define NSEMI 7
630#define NIF 8
631#define NWHILE 9
632#define NUNTIL 10
633#define NFOR 11
634#define NCASE 12
635#define NCLIST 13
636#define NDEFUN 14
637#define NARG 15
638#define NTO 16
639#if ENABLE_ASH_BASH_COMPAT
640#define NTO2 17
641#endif
642#define NCLOBBER 18
643#define NFROM 19
644#define NFROMTO 20
645#define NAPPEND 21
646#define NTOFD 22
647#define NFROMFD 23
648#define NHERE 24
649#define NXHERE 25
650#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000651#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000652
653union node;
654
655struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000656 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000657 union node *assign;
658 union node *args;
659 union node *redirect;
660};
661
662struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000663 smallint type;
664 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000665 struct nodelist *cmdlist;
666};
667
668struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000669 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000670 union node *n;
671 union node *redirect;
672};
673
674struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000675 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000676 union node *ch1;
677 union node *ch2;
678};
679
680struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000681 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000682 union node *test;
683 union node *ifpart;
684 union node *elsepart;
685};
686
687struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000688 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000689 union node *args;
690 union node *body;
691 char *var;
692};
693
694struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000695 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000696 union node *expr;
697 union node *cases;
698};
699
700struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000701 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000702 union node *next;
703 union node *pattern;
704 union node *body;
705};
706
707struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000708 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000709 union node *next;
710 char *text;
711 struct nodelist *backquote;
712};
713
Denis Vlasenko559691a2008-10-05 18:39:31 +0000714/* nfile and ndup layout must match!
715 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
716 * that it is actually NTO2 (>&file), and change its type.
717 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000718struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000719 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000720 union node *next;
721 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000722 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000723 union node *fname;
724 char *expfname;
725};
726
727struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000728 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000729 union node *next;
730 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000731 int dupfd;
732 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000733 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000734};
735
736struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000737 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000738 union node *next;
739 int fd;
740 union node *doc;
741};
742
743struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000744 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000745 union node *com;
746};
747
748union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000749 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000750 struct ncmd ncmd;
751 struct npipe npipe;
752 struct nredir nredir;
753 struct nbinary nbinary;
754 struct nif nif;
755 struct nfor nfor;
756 struct ncase ncase;
757 struct nclist nclist;
758 struct narg narg;
759 struct nfile nfile;
760 struct ndup ndup;
761 struct nhere nhere;
762 struct nnot nnot;
763};
764
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200765/*
766 * NODE_EOF is returned by parsecmd when it encounters an end of file.
767 * It must be distinct from NULL.
768 */
769#define NODE_EOF ((union node *) -1L)
770
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000771struct nodelist {
772 struct nodelist *next;
773 union node *n;
774};
775
776struct funcnode {
777 int count;
778 union node n;
779};
780
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000781/*
782 * Free a parse tree.
783 */
784static void
785freefunc(struct funcnode *f)
786{
787 if (f && --f->count < 0)
788 free(f);
789}
790
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000791
792/* ============ Debugging output */
793
794#if DEBUG
795
796static FILE *tracefile;
797
798static void
799trace_printf(const char *fmt, ...)
800{
801 va_list va;
802
803 if (debug != 1)
804 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000805 if (DEBUG_TIME)
806 fprintf(tracefile, "%u ", (int) time(NULL));
807 if (DEBUG_PID)
808 fprintf(tracefile, "[%u] ", (int) getpid());
809 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200810 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000811 va_start(va, fmt);
812 vfprintf(tracefile, fmt, va);
813 va_end(va);
814}
815
816static void
817trace_vprintf(const char *fmt, va_list va)
818{
819 if (debug != 1)
820 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000821 if (DEBUG_TIME)
822 fprintf(tracefile, "%u ", (int) time(NULL));
823 if (DEBUG_PID)
824 fprintf(tracefile, "[%u] ", (int) getpid());
825 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200826 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000827 vfprintf(tracefile, fmt, va);
828}
829
830static void
831trace_puts(const char *s)
832{
833 if (debug != 1)
834 return;
835 fputs(s, tracefile);
836}
837
838static void
839trace_puts_quoted(char *s)
840{
841 char *p;
842 char c;
843
844 if (debug != 1)
845 return;
846 putc('"', tracefile);
847 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100848 switch ((unsigned char)*p) {
849 case '\n': c = 'n'; goto backslash;
850 case '\t': c = 't'; goto backslash;
851 case '\r': c = 'r'; goto backslash;
852 case '\"': c = '\"'; goto backslash;
853 case '\\': c = '\\'; goto backslash;
854 case CTLESC: c = 'e'; goto backslash;
855 case CTLVAR: c = 'v'; goto backslash;
856 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
857 case CTLBACKQ: c = 'q'; goto backslash;
858 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000859 backslash:
860 putc('\\', tracefile);
861 putc(c, tracefile);
862 break;
863 default:
864 if (*p >= ' ' && *p <= '~')
865 putc(*p, tracefile);
866 else {
867 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100868 putc((*p >> 6) & 03, tracefile);
869 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000870 putc(*p & 07, tracefile);
871 }
872 break;
873 }
874 }
875 putc('"', tracefile);
876}
877
878static void
879trace_puts_args(char **ap)
880{
881 if (debug != 1)
882 return;
883 if (!*ap)
884 return;
885 while (1) {
886 trace_puts_quoted(*ap);
887 if (!*++ap) {
888 putc('\n', tracefile);
889 break;
890 }
891 putc(' ', tracefile);
892 }
893}
894
895static void
896opentrace(void)
897{
898 char s[100];
899#ifdef O_APPEND
900 int flags;
901#endif
902
903 if (debug != 1) {
904 if (tracefile)
905 fflush(tracefile);
906 /* leave open because libedit might be using it */
907 return;
908 }
909 strcpy(s, "./trace");
910 if (tracefile) {
911 if (!freopen(s, "a", tracefile)) {
912 fprintf(stderr, "Can't re-open %s\n", s);
913 debug = 0;
914 return;
915 }
916 } else {
917 tracefile = fopen(s, "a");
918 if (tracefile == NULL) {
919 fprintf(stderr, "Can't open %s\n", s);
920 debug = 0;
921 return;
922 }
923 }
924#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000925 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000926 if (flags >= 0)
927 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
928#endif
929 setlinebuf(tracefile);
930 fputs("\nTracing started.\n", tracefile);
931}
932
933static void
934indent(int amount, char *pfx, FILE *fp)
935{
936 int i;
937
938 for (i = 0; i < amount; i++) {
939 if (pfx && i == amount - 1)
940 fputs(pfx, fp);
941 putc('\t', fp);
942 }
943}
944
945/* little circular references here... */
946static void shtree(union node *n, int ind, char *pfx, FILE *fp);
947
948static void
949sharg(union node *arg, FILE *fp)
950{
951 char *p;
952 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100953 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000954
955 if (arg->type != NARG) {
956 out1fmt("<node type %d>\n", arg->type);
957 abort();
958 }
959 bqlist = arg->narg.backquote;
960 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100961 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000962 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700963 p++;
964 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000965 break;
966 case CTLVAR:
967 putc('$', fp);
968 putc('{', fp);
969 subtype = *++p;
970 if (subtype == VSLENGTH)
971 putc('#', fp);
972
Dan Fandrich77d48722010-09-07 23:38:28 -0700973 while (*p != '=') {
974 putc(*p, fp);
975 p++;
976 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000977
978 if (subtype & VSNUL)
979 putc(':', fp);
980
981 switch (subtype & VSTYPE) {
982 case VSNORMAL:
983 putc('}', fp);
984 break;
985 case VSMINUS:
986 putc('-', fp);
987 break;
988 case VSPLUS:
989 putc('+', fp);
990 break;
991 case VSQUESTION:
992 putc('?', fp);
993 break;
994 case VSASSIGN:
995 putc('=', fp);
996 break;
997 case VSTRIMLEFT:
998 putc('#', fp);
999 break;
1000 case VSTRIMLEFTMAX:
1001 putc('#', fp);
1002 putc('#', fp);
1003 break;
1004 case VSTRIMRIGHT:
1005 putc('%', fp);
1006 break;
1007 case VSTRIMRIGHTMAX:
1008 putc('%', fp);
1009 putc('%', fp);
1010 break;
1011 case VSLENGTH:
1012 break;
1013 default:
1014 out1fmt("<subtype %d>", subtype);
1015 }
1016 break;
1017 case CTLENDVAR:
1018 putc('}', fp);
1019 break;
1020 case CTLBACKQ:
1021 case CTLBACKQ|CTLQUOTE:
1022 putc('$', fp);
1023 putc('(', fp);
1024 shtree(bqlist->n, -1, NULL, fp);
1025 putc(')', fp);
1026 break;
1027 default:
1028 putc(*p, fp);
1029 break;
1030 }
1031 }
1032}
1033
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001034static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001035shcmd(union node *cmd, FILE *fp)
1036{
1037 union node *np;
1038 int first;
1039 const char *s;
1040 int dftfd;
1041
1042 first = 1;
1043 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001044 if (!first)
1045 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001046 sharg(np, fp);
1047 first = 0;
1048 }
1049 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001050 if (!first)
1051 putc(' ', fp);
1052 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001053 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001054 case NTO: s = ">>"+1; dftfd = 1; break;
1055 case NCLOBBER: s = ">|"; dftfd = 1; break;
1056 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001057#if ENABLE_ASH_BASH_COMPAT
1058 case NTO2:
1059#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001060 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001061 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001062 case NFROMFD: s = "<&"; break;
1063 case NFROMTO: s = "<>"; break;
1064 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001065 }
1066 if (np->nfile.fd != dftfd)
1067 fprintf(fp, "%d", np->nfile.fd);
1068 fputs(s, fp);
1069 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1070 fprintf(fp, "%d", np->ndup.dupfd);
1071 } else {
1072 sharg(np->nfile.fname, fp);
1073 }
1074 first = 0;
1075 }
1076}
1077
1078static void
1079shtree(union node *n, int ind, char *pfx, FILE *fp)
1080{
1081 struct nodelist *lp;
1082 const char *s;
1083
1084 if (n == NULL)
1085 return;
1086
1087 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001088
1089 if (n == NODE_EOF) {
1090 fputs("<EOF>", fp);
1091 return;
1092 }
1093
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001094 switch (n->type) {
1095 case NSEMI:
1096 s = "; ";
1097 goto binop;
1098 case NAND:
1099 s = " && ";
1100 goto binop;
1101 case NOR:
1102 s = " || ";
1103 binop:
1104 shtree(n->nbinary.ch1, ind, NULL, fp);
1105 /* if (ind < 0) */
1106 fputs(s, fp);
1107 shtree(n->nbinary.ch2, ind, NULL, fp);
1108 break;
1109 case NCMD:
1110 shcmd(n, fp);
1111 if (ind >= 0)
1112 putc('\n', fp);
1113 break;
1114 case NPIPE:
1115 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001116 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001117 if (lp->next)
1118 fputs(" | ", fp);
1119 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001120 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001121 fputs(" &", fp);
1122 if (ind >= 0)
1123 putc('\n', fp);
1124 break;
1125 default:
1126 fprintf(fp, "<node type %d>", n->type);
1127 if (ind >= 0)
1128 putc('\n', fp);
1129 break;
1130 }
1131}
1132
1133static void
1134showtree(union node *n)
1135{
1136 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001137 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001138}
1139
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001140#endif /* DEBUG */
1141
1142
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001143/* ============ Parser data */
1144
1145/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001146 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1147 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001148struct strlist {
1149 struct strlist *next;
1150 char *text;
1151};
1152
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001153struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001154
Denis Vlasenkob012b102007-02-19 22:43:01 +00001155struct strpush {
1156 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001157 char *prev_string;
1158 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001159#if ENABLE_ASH_ALIAS
1160 struct alias *ap; /* if push was associated with an alias */
1161#endif
1162 char *string; /* remember the string since it may change */
1163};
1164
1165struct parsefile {
1166 struct parsefile *prev; /* preceding file on stack */
1167 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001168 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001169 int left_in_line; /* number of chars left in this line */
1170 int left_in_buffer; /* number of chars left in this buffer past the line */
1171 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001172 char *buf; /* input buffer */
1173 struct strpush *strpush; /* for pushing strings at this level */
1174 struct strpush basestrpush; /* so pushing one is fast */
1175};
1176
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001177static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001178static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001179static int startlinno; /* line # where last token started */
1180static char *commandname; /* currently executing command */
1181static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001182static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001183
1184
1185/* ============ Message printing */
1186
1187static void
1188ash_vmsg(const char *msg, va_list ap)
1189{
1190 fprintf(stderr, "%s: ", arg0);
1191 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001192 if (strcmp(arg0, commandname))
1193 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001194 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001195 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001196 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001197 vfprintf(stderr, msg, ap);
1198 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001199}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001200
1201/*
1202 * Exverror is called to raise the error exception. If the second argument
1203 * is not NULL then error prints an error message using printf style
1204 * formatting. It then raises the error exception.
1205 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001206static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001207static void
1208ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001209{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001210#if DEBUG
1211 if (msg) {
1212 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1213 TRACEV((msg, ap));
1214 TRACE(("\") pid=%d\n", getpid()));
1215 } else
1216 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1217 if (msg)
1218#endif
1219 ash_vmsg(msg, ap);
1220
1221 flush_stdout_stderr();
1222 raise_exception(cond);
1223 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001224}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001225
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001226static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001227static void
1228ash_msg_and_raise_error(const char *msg, ...)
1229{
1230 va_list ap;
1231
1232 va_start(ap, msg);
1233 ash_vmsg_and_raise(EXERROR, msg, ap);
1234 /* NOTREACHED */
1235 va_end(ap);
1236}
1237
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001238static void raise_error_syntax(const char *) NORETURN;
1239static void
1240raise_error_syntax(const char *msg)
1241{
1242 ash_msg_and_raise_error("syntax error: %s", msg);
1243 /* NOTREACHED */
1244}
1245
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001246static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001247static void
1248ash_msg_and_raise(int cond, const char *msg, ...)
1249{
1250 va_list ap;
1251
1252 va_start(ap, msg);
1253 ash_vmsg_and_raise(cond, msg, ap);
1254 /* NOTREACHED */
1255 va_end(ap);
1256}
1257
1258/*
1259 * error/warning routines for external builtins
1260 */
1261static void
1262ash_msg(const char *fmt, ...)
1263{
1264 va_list ap;
1265
1266 va_start(ap, fmt);
1267 ash_vmsg(fmt, ap);
1268 va_end(ap);
1269}
1270
1271/*
1272 * Return a string describing an error. The returned string may be a
1273 * pointer to a static buffer that will be overwritten on the next call.
1274 * Action describes the operation that got the error.
1275 */
1276static const char *
1277errmsg(int e, const char *em)
1278{
1279 if (e == ENOENT || e == ENOTDIR) {
1280 return em;
1281 }
1282 return strerror(e);
1283}
1284
1285
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001286/* ============ Memory allocation */
1287
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001288#if 0
1289/* I consider these wrappers nearly useless:
1290 * ok, they return you to nearest exception handler, but
1291 * how much memory do you leak in the process, making
1292 * memory starvation worse?
1293 */
1294static void *
1295ckrealloc(void * p, size_t nbytes)
1296{
1297 p = realloc(p, nbytes);
1298 if (!p)
1299 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1300 return p;
1301}
1302
1303static void *
1304ckmalloc(size_t nbytes)
1305{
1306 return ckrealloc(NULL, nbytes);
1307}
1308
1309static void *
1310ckzalloc(size_t nbytes)
1311{
1312 return memset(ckmalloc(nbytes), 0, nbytes);
1313}
1314
1315static char *
1316ckstrdup(const char *s)
1317{
1318 char *p = strdup(s);
1319 if (!p)
1320 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1321 return p;
1322}
1323#else
1324/* Using bbox equivalents. They exit if out of memory */
1325# define ckrealloc xrealloc
1326# define ckmalloc xmalloc
1327# define ckzalloc xzalloc
1328# define ckstrdup xstrdup
1329#endif
1330
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331/*
1332 * It appears that grabstackstr() will barf with such alignments
1333 * because stalloc() will return a string allocated in a new stackblock.
1334 */
1335#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1336enum {
1337 /* Most machines require the value returned from malloc to be aligned
1338 * in some way. The following macro will get this right
1339 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001340 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001341 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001342 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001343};
1344
1345struct stack_block {
1346 struct stack_block *prev;
1347 char space[MINSIZE];
1348};
1349
1350struct stackmark {
1351 struct stack_block *stackp;
1352 char *stacknxt;
1353 size_t stacknleft;
1354 struct stackmark *marknext;
1355};
1356
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001357
Denis Vlasenko01631112007-12-16 17:20:38 +00001358struct globals_memstack {
1359 struct stack_block *g_stackp; // = &stackbase;
1360 struct stackmark *markp;
1361 char *g_stacknxt; // = stackbase.space;
1362 char *sstrend; // = stackbase.space + MINSIZE;
1363 size_t g_stacknleft; // = MINSIZE;
1364 int herefd; // = -1;
1365 struct stack_block stackbase;
1366};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001367extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1368#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001369#define g_stackp (G_memstack.g_stackp )
1370#define markp (G_memstack.markp )
1371#define g_stacknxt (G_memstack.g_stacknxt )
1372#define sstrend (G_memstack.sstrend )
1373#define g_stacknleft (G_memstack.g_stacknleft)
1374#define herefd (G_memstack.herefd )
1375#define stackbase (G_memstack.stackbase )
1376#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001377 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1378 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001379 g_stackp = &stackbase; \
1380 g_stacknxt = stackbase.space; \
1381 g_stacknleft = MINSIZE; \
1382 sstrend = stackbase.space + MINSIZE; \
1383 herefd = -1; \
1384} while (0)
1385
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001386
Denis Vlasenko01631112007-12-16 17:20:38 +00001387#define stackblock() ((void *)g_stacknxt)
1388#define stackblocksize() g_stacknleft
1389
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001390/*
1391 * Parse trees for commands are allocated in lifo order, so we use a stack
1392 * to make this more efficient, and also to avoid all sorts of exception
1393 * handling code to handle interrupts in the middle of a parse.
1394 *
1395 * The size 504 was chosen because the Ultrix malloc handles that size
1396 * well.
1397 */
1398static void *
1399stalloc(size_t nbytes)
1400{
1401 char *p;
1402 size_t aligned;
1403
1404 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001405 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001406 size_t len;
1407 size_t blocksize;
1408 struct stack_block *sp;
1409
1410 blocksize = aligned;
1411 if (blocksize < MINSIZE)
1412 blocksize = MINSIZE;
1413 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1414 if (len < blocksize)
1415 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1416 INT_OFF;
1417 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001418 sp->prev = g_stackp;
1419 g_stacknxt = sp->space;
1420 g_stacknleft = blocksize;
1421 sstrend = g_stacknxt + blocksize;
1422 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001423 INT_ON;
1424 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001425 p = g_stacknxt;
1426 g_stacknxt += aligned;
1427 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001428 return p;
1429}
1430
Denis Vlasenko597906c2008-02-20 16:38:54 +00001431static void *
1432stzalloc(size_t nbytes)
1433{
1434 return memset(stalloc(nbytes), 0, nbytes);
1435}
1436
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001437static void
1438stunalloc(void *p)
1439{
1440#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001441 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001442 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001443 abort();
1444 }
1445#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001446 g_stacknleft += g_stacknxt - (char *)p;
1447 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001448}
1449
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001450/*
1451 * Like strdup but works with the ash stack.
1452 */
1453static char *
1454ststrdup(const char *p)
1455{
1456 size_t len = strlen(p) + 1;
1457 return memcpy(stalloc(len), p, len);
1458}
1459
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001460static void
1461setstackmark(struct stackmark *mark)
1462{
Denis Vlasenko01631112007-12-16 17:20:38 +00001463 mark->stackp = g_stackp;
1464 mark->stacknxt = g_stacknxt;
1465 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001466 mark->marknext = markp;
1467 markp = mark;
1468}
1469
1470static void
1471popstackmark(struct stackmark *mark)
1472{
1473 struct stack_block *sp;
1474
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001475 if (!mark->stackp)
1476 return;
1477
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001478 INT_OFF;
1479 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001480 while (g_stackp != mark->stackp) {
1481 sp = g_stackp;
1482 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001483 free(sp);
1484 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001485 g_stacknxt = mark->stacknxt;
1486 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001487 sstrend = mark->stacknxt + mark->stacknleft;
1488 INT_ON;
1489}
1490
1491/*
1492 * When the parser reads in a string, it wants to stick the string on the
1493 * stack and only adjust the stack pointer when it knows how big the
1494 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1495 * of space on top of the stack and stackblocklen returns the length of
1496 * this block. Growstackblock will grow this space by at least one byte,
1497 * possibly moving it (like realloc). Grabstackblock actually allocates the
1498 * part of the block that has been used.
1499 */
1500static void
1501growstackblock(void)
1502{
1503 size_t newlen;
1504
Denis Vlasenko01631112007-12-16 17:20:38 +00001505 newlen = g_stacknleft * 2;
1506 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001507 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1508 if (newlen < 128)
1509 newlen += 128;
1510
Denis Vlasenko01631112007-12-16 17:20:38 +00001511 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001512 struct stack_block *oldstackp;
1513 struct stackmark *xmark;
1514 struct stack_block *sp;
1515 struct stack_block *prevstackp;
1516 size_t grosslen;
1517
1518 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001519 oldstackp = g_stackp;
1520 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001521 prevstackp = sp->prev;
1522 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1523 sp = ckrealloc(sp, grosslen);
1524 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001525 g_stackp = sp;
1526 g_stacknxt = sp->space;
1527 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001528 sstrend = sp->space + newlen;
1529
1530 /*
1531 * Stack marks pointing to the start of the old block
1532 * must be relocated to point to the new block
1533 */
1534 xmark = markp;
1535 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001536 xmark->stackp = g_stackp;
1537 xmark->stacknxt = g_stacknxt;
1538 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001539 xmark = xmark->marknext;
1540 }
1541 INT_ON;
1542 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001543 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001544 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001545 char *p = stalloc(newlen);
1546
1547 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001548 g_stacknxt = memcpy(p, oldspace, oldlen);
1549 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001550 }
1551}
1552
1553static void
1554grabstackblock(size_t len)
1555{
1556 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001557 g_stacknxt += len;
1558 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001559}
1560
1561/*
1562 * The following routines are somewhat easier to use than the above.
1563 * The user declares a variable of type STACKSTR, which may be declared
1564 * to be a register. The macro STARTSTACKSTR initializes things. Then
1565 * the user uses the macro STPUTC to add characters to the string. In
1566 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1567 * grown as necessary. When the user is done, she can just leave the
1568 * string there and refer to it using stackblock(). Or she can allocate
1569 * the space for it using grabstackstr(). If it is necessary to allow
1570 * someone else to use the stack temporarily and then continue to grow
1571 * the string, the user should use grabstack to allocate the space, and
1572 * then call ungrabstr(p) to return to the previous mode of operation.
1573 *
1574 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1575 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1576 * is space for at least one character.
1577 */
1578static void *
1579growstackstr(void)
1580{
1581 size_t len = stackblocksize();
1582 if (herefd >= 0 && len >= 1024) {
1583 full_write(herefd, stackblock(), len);
1584 return stackblock();
1585 }
1586 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001587 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001588}
1589
1590/*
1591 * Called from CHECKSTRSPACE.
1592 */
1593static char *
1594makestrspace(size_t newlen, char *p)
1595{
Denis Vlasenko01631112007-12-16 17:20:38 +00001596 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001597 size_t size = stackblocksize();
1598
1599 for (;;) {
1600 size_t nleft;
1601
1602 size = stackblocksize();
1603 nleft = size - len;
1604 if (nleft >= newlen)
1605 break;
1606 growstackblock();
1607 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001608 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001609}
1610
1611static char *
1612stack_nputstr(const char *s, size_t n, char *p)
1613{
1614 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001615 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001616 return p;
1617}
1618
1619static char *
1620stack_putstr(const char *s, char *p)
1621{
1622 return stack_nputstr(s, strlen(s), p);
1623}
1624
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001625static char *
1626_STPUTC(int c, char *p)
1627{
1628 if (p == sstrend)
1629 p = growstackstr();
1630 *p++ = c;
1631 return p;
1632}
1633
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001634#define STARTSTACKSTR(p) ((p) = stackblock())
1635#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001636#define CHECKSTRSPACE(n, p) do { \
1637 char *q = (p); \
1638 size_t l = (n); \
1639 size_t m = sstrend - q; \
1640 if (l > m) \
1641 (p) = makestrspace(l, q); \
1642} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001643#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001644#define STACKSTRNUL(p) do { \
1645 if ((p) == sstrend) \
1646 (p) = growstackstr(); \
1647 *(p) = '\0'; \
1648} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001649#define STUNPUTC(p) (--(p))
1650#define STTOPC(p) ((p)[-1])
1651#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001652
1653#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001654#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001655#define stackstrend() ((void *)sstrend)
1656
1657
1658/* ============ String helpers */
1659
1660/*
1661 * prefix -- see if pfx is a prefix of string.
1662 */
1663static char *
1664prefix(const char *string, const char *pfx)
1665{
1666 while (*pfx) {
1667 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001668 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001669 }
1670 return (char *) string;
1671}
1672
1673/*
1674 * Check for a valid number. This should be elsewhere.
1675 */
1676static int
1677is_number(const char *p)
1678{
1679 do {
1680 if (!isdigit(*p))
1681 return 0;
1682 } while (*++p != '\0');
1683 return 1;
1684}
1685
1686/*
1687 * Convert a string of digits to an integer, printing an error message on
1688 * failure.
1689 */
1690static int
1691number(const char *s)
1692{
1693 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001694 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001695 return atoi(s);
1696}
1697
1698/*
1699 * Produce a possibly single quoted string suitable as input to the shell.
1700 * The return string is allocated on the stack.
1701 */
1702static char *
1703single_quote(const char *s)
1704{
1705 char *p;
1706
1707 STARTSTACKSTR(p);
1708
1709 do {
1710 char *q;
1711 size_t len;
1712
1713 len = strchrnul(s, '\'') - s;
1714
1715 q = p = makestrspace(len + 3, p);
1716
1717 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001718 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001719 *q++ = '\'';
1720 s += len;
1721
1722 STADJUST(q - p, p);
1723
Denys Vlasenkocd716832009-11-28 22:14:02 +01001724 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001725 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001726 len = 0;
1727 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001728
1729 q = p = makestrspace(len + 3, p);
1730
1731 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001732 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734
1735 STADJUST(q - p, p);
1736 } while (*s);
1737
Denys Vlasenkocd716832009-11-28 22:14:02 +01001738 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001739
1740 return stackblock();
1741}
1742
1743
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001744/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745
1746static char **argptr; /* argument list for builtin commands */
1747static char *optionarg; /* set by nextopt (like getopt) */
1748static char *optptr; /* used by nextopt */
1749
1750/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001751 * XXX - should get rid of. Have all builtins use getopt(3).
1752 * The library getopt must have the BSD extension static variable
1753 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001755 * Standard option processing (a la getopt) for builtin routines.
1756 * The only argument that is passed to nextopt is the option string;
1757 * the other arguments are unnecessary. It returns the character,
1758 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759 */
1760static int
1761nextopt(const char *optstring)
1762{
1763 char *p;
1764 const char *q;
1765 char c;
1766
1767 p = optptr;
1768 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001769 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001770 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001771 if (p == NULL)
1772 return '\0';
1773 if (*p != '-')
1774 return '\0';
1775 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776 return '\0';
1777 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001778 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001779 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001780 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001781 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001782 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001784 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001786 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001787 if (*++q == ':')
1788 q++;
1789 }
1790 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001791 if (*p == '\0') {
1792 p = *argptr++;
1793 if (p == NULL)
1794 ash_msg_and_raise_error("no arg for -%c option", c);
1795 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001796 optionarg = p;
1797 p = NULL;
1798 }
1799 optptr = p;
1800 return c;
1801}
1802
1803
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001804/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001805
Denis Vlasenko01631112007-12-16 17:20:38 +00001806/*
1807 * The parsefile structure pointed to by the global variable parsefile
1808 * contains information about the current file being read.
1809 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001810struct shparam {
1811 int nparam; /* # of positional parameters (without $0) */
1812#if ENABLE_ASH_GETOPTS
1813 int optind; /* next parameter to be processed by getopts */
1814 int optoff; /* used by getopts */
1815#endif
1816 unsigned char malloced; /* if parameter list dynamically allocated */
1817 char **p; /* parameter list */
1818};
1819
1820/*
1821 * Free the list of positional parameters.
1822 */
1823static void
1824freeparam(volatile struct shparam *param)
1825{
Denis Vlasenko01631112007-12-16 17:20:38 +00001826 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001827 char **ap, **ap1;
1828 ap = ap1 = param->p;
1829 while (*ap)
1830 free(*ap++);
1831 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001832 }
1833}
1834
1835#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001836static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001837#endif
1838
1839struct var {
1840 struct var *next; /* next entry in hash list */
1841 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001842 const char *var_text; /* name=value */
1843 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001844 /* the variable gets set/unset */
1845};
1846
1847struct localvar {
1848 struct localvar *next; /* next local variable in list */
1849 struct var *vp; /* the variable that was made local */
1850 int flags; /* saved flags */
1851 const char *text; /* saved text */
1852};
1853
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001854/* flags */
1855#define VEXPORT 0x01 /* variable is exported */
1856#define VREADONLY 0x02 /* variable cannot be modified */
1857#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1858#define VTEXTFIXED 0x08 /* text is statically allocated */
1859#define VSTACK 0x10 /* text is allocated on the stack */
1860#define VUNSET 0x20 /* the variable is not set */
1861#define VNOFUNC 0x40 /* don't call the callback function */
1862#define VNOSET 0x80 /* do not set variable - just readonly test */
1863#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001864#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001865# define VDYNAMIC 0x200 /* dynamic variable */
1866#else
1867# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001868#endif
1869
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001870
Denis Vlasenko01631112007-12-16 17:20:38 +00001871/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001872#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001873static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001874change_lc_all(const char *value)
1875{
1876 if (value && *value != '\0')
1877 setlocale(LC_ALL, value);
1878}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001879static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001880change_lc_ctype(const char *value)
1881{
1882 if (value && *value != '\0')
1883 setlocale(LC_CTYPE, value);
1884}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001885#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001886#if ENABLE_ASH_MAIL
1887static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001888static void changemail(const char *var_value) FAST_FUNC;
1889#else
1890# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001892static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001893#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001894static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001895#endif
1896
Denis Vlasenko01631112007-12-16 17:20:38 +00001897static const struct {
1898 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001899 const char *var_text;
1900 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001901} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001902 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001903#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001904 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1905 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001906#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001907 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1908 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1909 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1910 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001912 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001913#endif
1914#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001915 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001916#endif
1917#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001918 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1919 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001920#endif
1921#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001922 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001923#endif
1924};
1925
Denis Vlasenko0b769642008-07-24 07:54:57 +00001926struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001927
1928struct globals_var {
1929 struct shparam shellparam; /* $@ current positional parameters */
1930 struct redirtab *redirlist;
1931 int g_nullredirs;
1932 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1933 struct var *vartab[VTABSIZE];
1934 struct var varinit[ARRAY_SIZE(varinit_data)];
1935};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001936extern struct globals_var *const ash_ptr_to_globals_var;
1937#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001938#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001939//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001940#define g_nullredirs (G_var.g_nullredirs )
1941#define preverrout_fd (G_var.preverrout_fd)
1942#define vartab (G_var.vartab )
1943#define varinit (G_var.varinit )
1944#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001945 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001946 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1947 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001948 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001949 varinit[i].flags = varinit_data[i].flags; \
1950 varinit[i].var_text = varinit_data[i].var_text; \
1951 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001952 } \
1953} while (0)
1954
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001955#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001956#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001957# define vmail (&vifs)[1]
1958# define vmpath (&vmail)[1]
1959# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001960#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001961# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001962#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001963#define vps1 (&vpath)[1]
1964#define vps2 (&vps1)[1]
1965#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001966#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001967# define voptind (&vps4)[1]
1968# if ENABLE_ASH_RANDOM_SUPPORT
1969# define vrandom (&voptind)[1]
1970# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001971#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001972# if ENABLE_ASH_RANDOM_SUPPORT
1973# define vrandom (&vps4)[1]
1974# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001975#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001976
1977/*
1978 * The following macros access the values of the above variables.
1979 * They have to skip over the name. They return the null string
1980 * for unset variables.
1981 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001982#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001983#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001984#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001985# define mailval() (vmail.var_text + 5)
1986# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001987# define mpathset() ((vmpath.flags & VUNSET) == 0)
1988#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001989#define pathval() (vpath.var_text + 5)
1990#define ps1val() (vps1.var_text + 4)
1991#define ps2val() (vps2.var_text + 4)
1992#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001993#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001994# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001995#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001996
Denis Vlasenko01631112007-12-16 17:20:38 +00001997#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001998static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001999getoptsreset(const char *value)
2000{
2001 shellparam.optind = number(value);
2002 shellparam.optoff = -1;
2003}
2004#endif
2005
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002006/* math.h has these, otherwise define our private copies */
2007#if !ENABLE_SH_MATH_SUPPORT
2008#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2009#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002010/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002011 * Return the pointer to the first char which is not part of a legal variable name
2012 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002013 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002014static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002015endofname(const char *name)
2016{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002017 if (!is_name(*name))
2018 return name;
2019 while (*++name) {
2020 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002021 break;
2022 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002023 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002024}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002025#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002026
2027/*
2028 * Compares two strings up to the first = or '\0'. The first
2029 * string must be terminated by '='; the second may be terminated by
2030 * either '=' or '\0'.
2031 */
2032static int
2033varcmp(const char *p, const char *q)
2034{
2035 int c, d;
2036
2037 while ((c = *p) == (d = *q)) {
2038 if (!c || c == '=')
2039 goto out;
2040 p++;
2041 q++;
2042 }
2043 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002044 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002045 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002046 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002047 out:
2048 return c - d;
2049}
2050
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002051/*
2052 * Find the appropriate entry in the hash table from the name.
2053 */
2054static struct var **
2055hashvar(const char *p)
2056{
2057 unsigned hashval;
2058
2059 hashval = ((unsigned char) *p) << 4;
2060 while (*p && *p != '=')
2061 hashval += (unsigned char) *p++;
2062 return &vartab[hashval % VTABSIZE];
2063}
2064
2065static int
2066vpcmp(const void *a, const void *b)
2067{
2068 return varcmp(*(const char **)a, *(const char **)b);
2069}
2070
2071/*
2072 * This routine initializes the builtin variables.
2073 */
2074static void
2075initvar(void)
2076{
2077 struct var *vp;
2078 struct var *end;
2079 struct var **vpp;
2080
2081 /*
2082 * PS1 depends on uid
2083 */
2084#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002085 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002086#else
2087 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002088 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002089#endif
2090 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002091 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002092 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002093 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094 vp->next = *vpp;
2095 *vpp = vp;
2096 } while (++vp < end);
2097}
2098
2099static struct var **
2100findvar(struct var **vpp, const char *name)
2101{
2102 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002103 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002104 break;
2105 }
2106 }
2107 return vpp;
2108}
2109
2110/*
2111 * Find the value of a variable. Returns NULL if not set.
2112 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002113static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002114lookupvar(const char *name)
2115{
2116 struct var *v;
2117
2118 v = *findvar(hashvar(name), name);
2119 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002120#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002121 /*
2122 * Dynamic variables are implemented roughly the same way they are
2123 * in bash. Namely, they're "special" so long as they aren't unset.
2124 * As soon as they're unset, they're no longer dynamic, and dynamic
2125 * lookup will no longer happen at that point. -- PFM.
2126 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002127 if (v->flags & VDYNAMIC)
2128 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002129#endif
2130 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002131 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002132 }
2133 return NULL;
2134}
2135
2136/*
2137 * Search the environment of a builtin command.
2138 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002139static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002140bltinlookup(const char *name)
2141{
2142 struct strlist *sp;
2143
2144 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002145 if (varcmp(sp->text, name) == 0)
2146 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002147 }
2148 return lookupvar(name);
2149}
2150
2151/*
2152 * Same as setvar except that the variable and value are passed in
2153 * the first argument as name=value. Since the first argument will
2154 * be actually stored in the table, it should not be a string that
2155 * will go away.
2156 * Called with interrupts off.
2157 */
2158static void
2159setvareq(char *s, int flags)
2160{
2161 struct var *vp, **vpp;
2162
2163 vpp = hashvar(s);
2164 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2165 vp = *findvar(vpp, s);
2166 if (vp) {
2167 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2168 const char *n;
2169
2170 if (flags & VNOSAVE)
2171 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002172 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002173 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2174 }
2175
2176 if (flags & VNOSET)
2177 return;
2178
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002179 if (vp->var_func && !(flags & VNOFUNC))
2180 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002181
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002182 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2183 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002184
2185 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2186 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002187 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002188 if (flags & VNOSET)
2189 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002190 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002191 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002192 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002193 *vpp = vp;
2194 }
2195 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2196 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002197 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002198 vp->flags = flags;
2199}
2200
2201/*
2202 * Set the value of a variable. The flags argument is ored with the
2203 * flags of the variable. If val is NULL, the variable is unset.
2204 */
2205static void
2206setvar(const char *name, const char *val, int flags)
2207{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002208 const char *q;
2209 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002210 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002211 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002212 size_t vallen;
2213
2214 q = endofname(name);
2215 p = strchrnul(q, '=');
2216 namelen = p - name;
2217 if (!namelen || p != q)
2218 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2219 vallen = 0;
2220 if (val == NULL) {
2221 flags |= VUNSET;
2222 } else {
2223 vallen = strlen(val);
2224 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002225
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002226 INT_OFF;
2227 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002228 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002229 if (val) {
2230 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002231 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002232 }
2233 *p = '\0';
2234 setvareq(nameeq, flags | VNOSAVE);
2235 INT_ON;
2236}
2237
Denys Vlasenko03dad222010-01-12 23:29:57 +01002238static void FAST_FUNC
2239setvar2(const char *name, const char *val)
2240{
2241 setvar(name, val, 0);
2242}
2243
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002244#if ENABLE_ASH_GETOPTS
2245/*
2246 * Safe version of setvar, returns 1 on success 0 on failure.
2247 */
2248static int
2249setvarsafe(const char *name, const char *val, int flags)
2250{
2251 int err;
2252 volatile int saveint;
2253 struct jmploc *volatile savehandler = exception_handler;
2254 struct jmploc jmploc;
2255
2256 SAVE_INT(saveint);
2257 if (setjmp(jmploc.loc))
2258 err = 1;
2259 else {
2260 exception_handler = &jmploc;
2261 setvar(name, val, flags);
2262 err = 0;
2263 }
2264 exception_handler = savehandler;
2265 RESTORE_INT(saveint);
2266 return err;
2267}
2268#endif
2269
2270/*
2271 * Unset the specified variable.
2272 */
2273static int
2274unsetvar(const char *s)
2275{
2276 struct var **vpp;
2277 struct var *vp;
2278 int retval;
2279
2280 vpp = findvar(hashvar(s), s);
2281 vp = *vpp;
2282 retval = 2;
2283 if (vp) {
2284 int flags = vp->flags;
2285
2286 retval = 1;
2287 if (flags & VREADONLY)
2288 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002289#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002290 vp->flags &= ~VDYNAMIC;
2291#endif
2292 if (flags & VUNSET)
2293 goto ok;
2294 if ((flags & VSTRFIXED) == 0) {
2295 INT_OFF;
2296 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002297 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002298 *vpp = vp->next;
2299 free(vp);
2300 INT_ON;
2301 } else {
2302 setvar(s, 0, 0);
2303 vp->flags &= ~VEXPORT;
2304 }
2305 ok:
2306 retval = 0;
2307 }
2308 out:
2309 return retval;
2310}
2311
2312/*
2313 * Process a linked list of variable assignments.
2314 */
2315static void
2316listsetvar(struct strlist *list_set_var, int flags)
2317{
2318 struct strlist *lp = list_set_var;
2319
2320 if (!lp)
2321 return;
2322 INT_OFF;
2323 do {
2324 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002325 lp = lp->next;
2326 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002327 INT_ON;
2328}
2329
2330/*
2331 * Generate a list of variables satisfying the given conditions.
2332 */
2333static char **
2334listvars(int on, int off, char ***end)
2335{
2336 struct var **vpp;
2337 struct var *vp;
2338 char **ep;
2339 int mask;
2340
2341 STARTSTACKSTR(ep);
2342 vpp = vartab;
2343 mask = on | off;
2344 do {
2345 for (vp = *vpp; vp; vp = vp->next) {
2346 if ((vp->flags & mask) == on) {
2347 if (ep == stackstrend())
2348 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002349 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002350 }
2351 }
2352 } while (++vpp < vartab + VTABSIZE);
2353 if (ep == stackstrend())
2354 ep = growstackstr();
2355 if (end)
2356 *end = ep;
2357 *ep++ = NULL;
2358 return grabstackstr(ep);
2359}
2360
2361
2362/* ============ Path search helper
2363 *
2364 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002365 * of the path before the first call; path_advance will update
2366 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002367 * the possible path expansions in sequence. If an option (indicated by
2368 * a percent sign) appears in the path entry then the global variable
2369 * pathopt will be set to point to it; otherwise pathopt will be set to
2370 * NULL.
2371 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002372static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002373
2374static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002375path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002376{
2377 const char *p;
2378 char *q;
2379 const char *start;
2380 size_t len;
2381
2382 if (*path == NULL)
2383 return NULL;
2384 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002385 for (p = start; *p && *p != ':' && *p != '%'; p++)
2386 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002387 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2388 while (stackblocksize() < len)
2389 growstackblock();
2390 q = stackblock();
2391 if (p != start) {
2392 memcpy(q, start, p - start);
2393 q += p - start;
2394 *q++ = '/';
2395 }
2396 strcpy(q, name);
2397 pathopt = NULL;
2398 if (*p == '%') {
2399 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002400 while (*p && *p != ':')
2401 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002402 }
2403 if (*p == ':')
2404 *path = p + 1;
2405 else
2406 *path = NULL;
2407 return stalloc(len);
2408}
2409
2410
2411/* ============ Prompt */
2412
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002413static smallint doprompt; /* if set, prompt the user */
2414static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002415
2416#if ENABLE_FEATURE_EDITING
2417static line_input_t *line_input_state;
2418static const char *cmdedit_prompt;
2419static void
2420putprompt(const char *s)
2421{
2422 if (ENABLE_ASH_EXPAND_PRMT) {
2423 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002424 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002425 return;
2426 }
2427 cmdedit_prompt = s;
2428}
2429#else
2430static void
2431putprompt(const char *s)
2432{
2433 out2str(s);
2434}
2435#endif
2436
2437#if ENABLE_ASH_EXPAND_PRMT
2438/* expandstr() needs parsing machinery, so it is far away ahead... */
2439static const char *expandstr(const char *ps);
2440#else
2441#define expandstr(s) s
2442#endif
2443
2444static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002445setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002446{
2447 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002448 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2449
2450 if (!do_set)
2451 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002452
2453 needprompt = 0;
2454
2455 switch (whichprompt) {
2456 case 1:
2457 prompt = ps1val();
2458 break;
2459 case 2:
2460 prompt = ps2val();
2461 break;
2462 default: /* 0 */
2463 prompt = nullstr;
2464 }
2465#if ENABLE_ASH_EXPAND_PRMT
2466 setstackmark(&smark);
2467 stalloc(stackblocksize());
2468#endif
2469 putprompt(expandstr(prompt));
2470#if ENABLE_ASH_EXPAND_PRMT
2471 popstackmark(&smark);
2472#endif
2473}
2474
2475
2476/* ============ The cd and pwd commands */
2477
2478#define CD_PHYSICAL 1
2479#define CD_PRINT 2
2480
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002481static int
2482cdopt(void)
2483{
2484 int flags = 0;
2485 int i, j;
2486
2487 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002488 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002489 if (i != j) {
2490 flags ^= CD_PHYSICAL;
2491 j = i;
2492 }
2493 }
2494
2495 return flags;
2496}
2497
2498/*
2499 * Update curdir (the name of the current directory) in response to a
2500 * cd command.
2501 */
2502static const char *
2503updatepwd(const char *dir)
2504{
2505 char *new;
2506 char *p;
2507 char *cdcomppath;
2508 const char *lim;
2509
2510 cdcomppath = ststrdup(dir);
2511 STARTSTACKSTR(new);
2512 if (*dir != '/') {
2513 if (curdir == nullstr)
2514 return 0;
2515 new = stack_putstr(curdir, new);
2516 }
2517 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002518 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002519 if (*dir != '/') {
2520 if (new[-1] != '/')
2521 USTPUTC('/', new);
2522 if (new > lim && *lim == '/')
2523 lim++;
2524 } else {
2525 USTPUTC('/', new);
2526 cdcomppath++;
2527 if (dir[1] == '/' && dir[2] != '/') {
2528 USTPUTC('/', new);
2529 cdcomppath++;
2530 lim++;
2531 }
2532 }
2533 p = strtok(cdcomppath, "/");
2534 while (p) {
2535 switch (*p) {
2536 case '.':
2537 if (p[1] == '.' && p[2] == '\0') {
2538 while (new > lim) {
2539 STUNPUTC(new);
2540 if (new[-1] == '/')
2541 break;
2542 }
2543 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002544 }
2545 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002546 break;
2547 /* fall through */
2548 default:
2549 new = stack_putstr(p, new);
2550 USTPUTC('/', new);
2551 }
2552 p = strtok(0, "/");
2553 }
2554 if (new > lim)
2555 STUNPUTC(new);
2556 *new = 0;
2557 return stackblock();
2558}
2559
2560/*
2561 * Find out what the current directory is. If we already know the current
2562 * directory, this routine returns immediately.
2563 */
2564static char *
2565getpwd(void)
2566{
Denis Vlasenko01631112007-12-16 17:20:38 +00002567 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002568 return dir ? dir : nullstr;
2569}
2570
2571static void
2572setpwd(const char *val, int setold)
2573{
2574 char *oldcur, *dir;
2575
2576 oldcur = dir = curdir;
2577
2578 if (setold) {
2579 setvar("OLDPWD", oldcur, VEXPORT);
2580 }
2581 INT_OFF;
2582 if (physdir != nullstr) {
2583 if (physdir != oldcur)
2584 free(physdir);
2585 physdir = nullstr;
2586 }
2587 if (oldcur == val || !val) {
2588 char *s = getpwd();
2589 physdir = s;
2590 if (!val)
2591 dir = s;
2592 } else
2593 dir = ckstrdup(val);
2594 if (oldcur != dir && oldcur != nullstr) {
2595 free(oldcur);
2596 }
2597 curdir = dir;
2598 INT_ON;
2599 setvar("PWD", dir, VEXPORT);
2600}
2601
2602static void hashcd(void);
2603
2604/*
2605 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2606 * know that the current directory has changed.
2607 */
2608static int
2609docd(const char *dest, int flags)
2610{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002611 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002612 int err;
2613
2614 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2615
2616 INT_OFF;
2617 if (!(flags & CD_PHYSICAL)) {
2618 dir = updatepwd(dest);
2619 if (dir)
2620 dest = dir;
2621 }
2622 err = chdir(dest);
2623 if (err)
2624 goto out;
2625 setpwd(dir, 1);
2626 hashcd();
2627 out:
2628 INT_ON;
2629 return err;
2630}
2631
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002632static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002633cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002634{
2635 const char *dest;
2636 const char *path;
2637 const char *p;
2638 char c;
2639 struct stat statb;
2640 int flags;
2641
2642 flags = cdopt();
2643 dest = *argptr;
2644 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002645 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002646 else if (LONE_DASH(dest)) {
2647 dest = bltinlookup("OLDPWD");
2648 flags |= CD_PRINT;
2649 }
2650 if (!dest)
2651 dest = nullstr;
2652 if (*dest == '/')
2653 goto step7;
2654 if (*dest == '.') {
2655 c = dest[1];
2656 dotdot:
2657 switch (c) {
2658 case '\0':
2659 case '/':
2660 goto step6;
2661 case '.':
2662 c = dest[2];
2663 if (c != '.')
2664 goto dotdot;
2665 }
2666 }
2667 if (!*dest)
2668 dest = ".";
2669 path = bltinlookup("CDPATH");
2670 if (!path) {
2671 step6:
2672 step7:
2673 p = dest;
2674 goto docd;
2675 }
2676 do {
2677 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002678 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002679 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2680 if (c && c != ':')
2681 flags |= CD_PRINT;
2682 docd:
2683 if (!docd(p, flags))
2684 goto out;
2685 break;
2686 }
2687 } while (path);
2688 ash_msg_and_raise_error("can't cd to %s", dest);
2689 /* NOTREACHED */
2690 out:
2691 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002692 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002693 return 0;
2694}
2695
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002696static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002697pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002698{
2699 int flags;
2700 const char *dir = curdir;
2701
2702 flags = cdopt();
2703 if (flags) {
2704 if (physdir == nullstr)
2705 setpwd(dir, 0);
2706 dir = physdir;
2707 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002708 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002709 return 0;
2710}
2711
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002712
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002713/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002714
Denis Vlasenko834dee72008-10-07 09:18:30 +00002715
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002716#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002717
Eric Andersenc470f442003-07-28 09:56:35 +00002718/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002719#define CWORD 0 /* character is nothing special */
2720#define CNL 1 /* newline character */
2721#define CBACK 2 /* a backslash character */
2722#define CSQUOTE 3 /* single quote */
2723#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002724#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002725#define CBQUOTE 6 /* backwards single quote */
2726#define CVAR 7 /* a dollar sign */
2727#define CENDVAR 8 /* a '}' character */
2728#define CLP 9 /* a left paren in arithmetic */
2729#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002730#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002731#define CCTL 12 /* like CWORD, except it must be escaped */
2732#define CSPCL 13 /* these terminate a word */
2733#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002734
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002735#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002736#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002737# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002738#endif
2739
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002740#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002741
Mike Frysinger98c52642009-04-02 10:02:37 +00002742#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002743# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002744#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002745# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002746#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002747static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002748#if ENABLE_ASH_ALIAS
2749 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2750#endif
2751 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2752 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2753 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2754 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2755 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2756 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2757 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2758 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2759 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2760 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2761 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002762#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002763 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2764 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2765 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2766#endif
2767#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002768};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002769/* Constants below must match table above */
2770enum {
2771#if ENABLE_ASH_ALIAS
2772 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2773#endif
2774 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2775 CNL_CNL_CNL_CNL , /* 2 */
2776 CWORD_CCTL_CCTL_CWORD , /* 3 */
2777 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2778 CVAR_CVAR_CWORD_CVAR , /* 5 */
2779 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2780 CSPCL_CWORD_CWORD_CLP , /* 7 */
2781 CSPCL_CWORD_CWORD_CRP , /* 8 */
2782 CBACK_CBACK_CCTL_CBACK , /* 9 */
2783 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2784 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2785 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2786 CWORD_CWORD_CWORD_CWORD , /* 13 */
2787 CCTL_CCTL_CCTL_CCTL , /* 14 */
2788};
Eric Andersen2870d962001-07-02 17:27:21 +00002789
Denys Vlasenkocd716832009-11-28 22:14:02 +01002790/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2791 * caller must ensure proper cast on it if c is *char_ptr!
2792 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002793/* Values for syntax param */
2794#define BASESYNTAX 0 /* not in quotes */
2795#define DQSYNTAX 1 /* in double quotes */
2796#define SQSYNTAX 2 /* in single quotes */
2797#define ARISYNTAX 3 /* in arithmetic */
2798#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002799
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002800#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002801
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002802static int
2803SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002804{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002805 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002806# if ENABLE_ASH_ALIAS
2807 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002808 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2809 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2810 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2811 11, 3 /* "}~" */
2812 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002813# else
2814 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002815 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2816 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2817 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2818 10, 2 /* "}~" */
2819 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002820# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002821 const char *s;
2822 int indx;
2823
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002824 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002825 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002826# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002827 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002828 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002829 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002830# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002831 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002832 /* Cast is purely for paranoia here,
2833 * just in case someone passed signed char to us */
2834 if ((unsigned char)c >= CTL_FIRST
2835 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002836 ) {
2837 return CCTL;
2838 }
2839 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002840 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002841 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002842 indx = syntax_index_table[s - spec_symbls];
2843 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002844 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002845}
2846
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002847#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002848
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002849static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002850 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002851 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2861 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2862 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2884 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2885 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2886 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2888 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2890 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2891 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2892 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2893 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2894 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2896 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2897 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2898 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2899 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2910 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2911 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2912 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2914 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2915 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2943 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2944 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2945 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2948 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2976 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2977 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2978 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2979 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2980 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2981 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2982 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2983 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2984 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2985 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2986 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2987 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2988 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3105 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3106 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003107 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003108# if ENABLE_ASH_ALIAS
3109 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3110# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003111};
3112
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003113# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003114
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003115#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003116
Eric Andersen2870d962001-07-02 17:27:21 +00003117
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003118/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003119
Denis Vlasenko131ae172007-02-18 13:00:19 +00003120#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003121
3122#define ALIASINUSE 1
3123#define ALIASDEAD 2
3124
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003125struct alias {
3126 struct alias *next;
3127 char *name;
3128 char *val;
3129 int flag;
3130};
3131
Denis Vlasenko01631112007-12-16 17:20:38 +00003132
3133static struct alias **atab; // [ATABSIZE];
3134#define INIT_G_alias() do { \
3135 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3136} while (0)
3137
Eric Andersen2870d962001-07-02 17:27:21 +00003138
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003139static struct alias **
3140__lookupalias(const char *name) {
3141 unsigned int hashval;
3142 struct alias **app;
3143 const char *p;
3144 unsigned int ch;
3145
3146 p = name;
3147
3148 ch = (unsigned char)*p;
3149 hashval = ch << 4;
3150 while (ch) {
3151 hashval += ch;
3152 ch = (unsigned char)*++p;
3153 }
3154 app = &atab[hashval % ATABSIZE];
3155
3156 for (; *app; app = &(*app)->next) {
3157 if (strcmp(name, (*app)->name) == 0) {
3158 break;
3159 }
3160 }
3161
3162 return app;
3163}
3164
3165static struct alias *
3166lookupalias(const char *name, int check)
3167{
3168 struct alias *ap = *__lookupalias(name);
3169
3170 if (check && ap && (ap->flag & ALIASINUSE))
3171 return NULL;
3172 return ap;
3173}
3174
3175static struct alias *
3176freealias(struct alias *ap)
3177{
3178 struct alias *next;
3179
3180 if (ap->flag & ALIASINUSE) {
3181 ap->flag |= ALIASDEAD;
3182 return ap;
3183 }
3184
3185 next = ap->next;
3186 free(ap->name);
3187 free(ap->val);
3188 free(ap);
3189 return next;
3190}
Eric Andersencb57d552001-06-28 07:25:16 +00003191
Eric Andersenc470f442003-07-28 09:56:35 +00003192static void
3193setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003194{
3195 struct alias *ap, **app;
3196
3197 app = __lookupalias(name);
3198 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003199 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003200 if (ap) {
3201 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003202 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003203 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003204 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003205 ap->flag &= ~ALIASDEAD;
3206 } else {
3207 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003208 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003209 ap->name = ckstrdup(name);
3210 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003211 /*ap->flag = 0; - ckzalloc did it */
3212 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003213 *app = ap;
3214 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003215 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003216}
3217
Eric Andersenc470f442003-07-28 09:56:35 +00003218static int
3219unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003220{
Eric Andersencb57d552001-06-28 07:25:16 +00003221 struct alias **app;
3222
3223 app = __lookupalias(name);
3224
3225 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003226 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003227 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003228 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003229 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003230 }
3231
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003232 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003233}
3234
Eric Andersenc470f442003-07-28 09:56:35 +00003235static void
3236rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003237{
Eric Andersencb57d552001-06-28 07:25:16 +00003238 struct alias *ap, **app;
3239 int i;
3240
Denis Vlasenkob012b102007-02-19 22:43:01 +00003241 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003242 for (i = 0; i < ATABSIZE; i++) {
3243 app = &atab[i];
3244 for (ap = *app; ap; ap = *app) {
3245 *app = freealias(*app);
3246 if (ap == *app) {
3247 app = &ap->next;
3248 }
3249 }
3250 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003251 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003252}
3253
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003254static void
3255printalias(const struct alias *ap)
3256{
3257 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3258}
3259
Eric Andersencb57d552001-06-28 07:25:16 +00003260/*
3261 * TODO - sort output
3262 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003263static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003264aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003265{
3266 char *n, *v;
3267 int ret = 0;
3268 struct alias *ap;
3269
Denis Vlasenko68404f12008-03-17 09:00:54 +00003270 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003271 int i;
3272
Denis Vlasenko68404f12008-03-17 09:00:54 +00003273 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003274 for (ap = atab[i]; ap; ap = ap->next) {
3275 printalias(ap);
3276 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003277 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003278 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003279 }
3280 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003281 v = strchr(n+1, '=');
3282 if (v == NULL) { /* n+1: funny ksh stuff */
3283 ap = *__lookupalias(n);
3284 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003285 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003286 ret = 1;
3287 } else
3288 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003289 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003290 *v++ = '\0';
3291 setalias(n, v);
3292 }
3293 }
3294
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003295 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003296}
3297
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003298static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003299unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003300{
3301 int i;
3302
3303 while ((i = nextopt("a")) != '\0') {
3304 if (i == 'a') {
3305 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003306 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003307 }
3308 }
3309 for (i = 0; *argptr; argptr++) {
3310 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003311 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003312 i = 1;
3313 }
3314 }
3315
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003316 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003317}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003318
Denis Vlasenko131ae172007-02-18 13:00:19 +00003319#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003320
Eric Andersenc470f442003-07-28 09:56:35 +00003321
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003322/* ============ jobs.c */
3323
3324/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003325#define FORK_FG 0
3326#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327#define FORK_NOJOB 2
3328
3329/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003330#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3331#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3332#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003333
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003334/*
3335 * A job structure contains information about a job. A job is either a
3336 * single process or a set of processes contained in a pipeline. In the
3337 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3338 * array of pids.
3339 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003340struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003341 pid_t ps_pid; /* process id */
3342 int ps_status; /* last process status from wait() */
3343 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003344};
3345
3346struct job {
3347 struct procstat ps0; /* status of process */
3348 struct procstat *ps; /* status or processes when more than one */
3349#if JOBS
3350 int stopstatus; /* status of a stopped job */
3351#endif
3352 uint32_t
3353 nprocs: 16, /* number of processes */
3354 state: 8,
3355#define JOBRUNNING 0 /* at least one proc running */
3356#define JOBSTOPPED 1 /* all procs are stopped */
3357#define JOBDONE 2 /* all procs are completed */
3358#if JOBS
3359 sigint: 1, /* job was killed by SIGINT */
3360 jobctl: 1, /* job running under job control */
3361#endif
3362 waited: 1, /* true if this entry has been waited for */
3363 used: 1, /* true if this entry is in used */
3364 changed: 1; /* true if status has changed */
3365 struct job *prev_job; /* previous job */
3366};
3367
Denis Vlasenko68404f12008-03-17 09:00:54 +00003368static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003369static int forkshell(struct job *, union node *, int);
3370static int waitforjob(struct job *);
3371
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003372#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003373enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003374#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003375#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003376static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003377static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003378#endif
3379
3380/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003381 * Ignore a signal.
3382 */
3383static void
3384ignoresig(int signo)
3385{
3386 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3387 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3388 /* No, need to do it */
3389 signal(signo, SIG_IGN);
3390 }
3391 sigmode[signo - 1] = S_HARD_IGN;
3392}
3393
3394/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003395 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003396 */
3397static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003398signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003399{
3400 gotsig[signo - 1] = 1;
3401
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003402 if (signo == SIGINT && !trap[SIGINT]) {
3403 if (!suppress_int) {
3404 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003405 raise_interrupt(); /* does not return */
3406 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003407 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003408 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003409 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003410 }
3411}
3412
3413/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003414 * Set the signal handler for the specified signal. The routine figures
3415 * out what it should be set to.
3416 */
3417static void
3418setsignal(int signo)
3419{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003420 char *t;
3421 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003422 struct sigaction act;
3423
3424 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003425 new_act = S_DFL;
3426 if (t != NULL) { /* trap for this sig is set */
3427 new_act = S_CATCH;
3428 if (t[0] == '\0') /* trap is "": ignore this sig */
3429 new_act = S_IGN;
3430 }
3431
3432 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003433 switch (signo) {
3434 case SIGINT:
3435 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003436 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003437 break;
3438 case SIGQUIT:
3439#if DEBUG
3440 if (debug)
3441 break;
3442#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003443 /* man bash:
3444 * "In all cases, bash ignores SIGQUIT. Non-builtin
3445 * commands run by bash have signal handlers
3446 * set to the values inherited by the shell
3447 * from its parent". */
3448 new_act = S_IGN;
3449 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003450 case SIGTERM:
3451 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003452 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003453 break;
3454#if JOBS
3455 case SIGTSTP:
3456 case SIGTTOU:
3457 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003458 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003459 break;
3460#endif
3461 }
3462 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003463//TODO: if !rootshell, we reset SIGQUIT to DFL,
3464//whereas we have to restore it to what shell got on entry
3465//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003466
3467 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003468 cur_act = *t;
3469 if (cur_act == 0) {
3470 /* current setting is not yet known */
3471 if (sigaction(signo, NULL, &act)) {
3472 /* pretend it worked; maybe we should give a warning,
3473 * but other shells don't. We don't alter sigmode,
3474 * so we retry every time.
3475 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003476 return;
3477 }
3478 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003479 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003480 if (mflag
3481 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3482 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003483 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003484 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003485 }
3486 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003487 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003488 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003489
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003490 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003491 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003492 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003493 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003494 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3495 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003496 break;
3497 case S_IGN:
3498 act.sa_handler = SIG_IGN;
3499 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003500 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003501 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003502
3503 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003504}
3505
3506/* mode flags for set_curjob */
3507#define CUR_DELETE 2
3508#define CUR_RUNNING 1
3509#define CUR_STOPPED 0
3510
3511/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003512#define DOWAIT_NONBLOCK WNOHANG
3513#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003514
3515#if JOBS
3516/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003517static int initialpgrp; //references:2
3518static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003519#endif
3520/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003521static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003522/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003523static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003524/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003525static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003526/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003527static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003528
3529static void
3530set_curjob(struct job *jp, unsigned mode)
3531{
3532 struct job *jp1;
3533 struct job **jpp, **curp;
3534
3535 /* first remove from list */
3536 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003537 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003538 jp1 = *jpp;
3539 if (jp1 == jp)
3540 break;
3541 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003542 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003543 *jpp = jp1->prev_job;
3544
3545 /* Then re-insert in correct position */
3546 jpp = curp;
3547 switch (mode) {
3548 default:
3549#if DEBUG
3550 abort();
3551#endif
3552 case CUR_DELETE:
3553 /* job being deleted */
3554 break;
3555 case CUR_RUNNING:
3556 /* newly created job or backgrounded job,
3557 put after all stopped jobs. */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003558 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003559 jp1 = *jpp;
3560#if JOBS
3561 if (!jp1 || jp1->state != JOBSTOPPED)
3562#endif
3563 break;
3564 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003565 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003566 /* FALLTHROUGH */
3567#if JOBS
3568 case CUR_STOPPED:
3569#endif
3570 /* newly stopped job - becomes curjob */
3571 jp->prev_job = *jpp;
3572 *jpp = jp;
3573 break;
3574 }
3575}
3576
3577#if JOBS || DEBUG
3578static int
3579jobno(const struct job *jp)
3580{
3581 return jp - jobtab + 1;
3582}
3583#endif
3584
3585/*
3586 * Convert a job name to a job structure.
3587 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003588#if !JOBS
3589#define getjob(name, getctl) getjob(name)
3590#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003591static struct job *
3592getjob(const char *name, int getctl)
3593{
3594 struct job *jp;
3595 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003596 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003597 unsigned num;
3598 int c;
3599 const char *p;
3600 char *(*match)(const char *, const char *);
3601
3602 jp = curjob;
3603 p = name;
3604 if (!p)
3605 goto currentjob;
3606
3607 if (*p != '%')
3608 goto err;
3609
3610 c = *++p;
3611 if (!c)
3612 goto currentjob;
3613
3614 if (!p[1]) {
3615 if (c == '+' || c == '%') {
3616 currentjob:
3617 err_msg = "No current job";
3618 goto check;
3619 }
3620 if (c == '-') {
3621 if (jp)
3622 jp = jp->prev_job;
3623 err_msg = "No previous job";
3624 check:
3625 if (!jp)
3626 goto err;
3627 goto gotit;
3628 }
3629 }
3630
3631 if (is_number(p)) {
3632 num = atoi(p);
3633 if (num < njobs) {
3634 jp = jobtab + num - 1;
3635 if (jp->used)
3636 goto gotit;
3637 goto err;
3638 }
3639 }
3640
3641 match = prefix;
3642 if (*p == '?') {
3643 match = strstr;
3644 p++;
3645 }
3646
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003647 found = NULL;
3648 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003649 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003650 if (found)
3651 goto err;
3652 found = jp;
3653 err_msg = "%s: ambiguous";
3654 }
3655 jp = jp->prev_job;
3656 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003657 if (!found)
3658 goto err;
3659 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003660
3661 gotit:
3662#if JOBS
3663 err_msg = "job %s not created under job control";
3664 if (getctl && jp->jobctl == 0)
3665 goto err;
3666#endif
3667 return jp;
3668 err:
3669 ash_msg_and_raise_error(err_msg, name);
3670}
3671
3672/*
3673 * Mark a job structure as unused.
3674 */
3675static void
3676freejob(struct job *jp)
3677{
3678 struct procstat *ps;
3679 int i;
3680
3681 INT_OFF;
3682 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003683 if (ps->ps_cmd != nullstr)
3684 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003685 }
3686 if (jp->ps != &jp->ps0)
3687 free(jp->ps);
3688 jp->used = 0;
3689 set_curjob(jp, CUR_DELETE);
3690 INT_ON;
3691}
3692
3693#if JOBS
3694static void
3695xtcsetpgrp(int fd, pid_t pgrp)
3696{
3697 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003698 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003699}
3700
3701/*
3702 * Turn job control on and off.
3703 *
3704 * Note: This code assumes that the third arg to ioctl is a character
3705 * pointer, which is true on Berkeley systems but not System V. Since
3706 * System V doesn't have job control yet, this isn't a problem now.
3707 *
3708 * Called with interrupts off.
3709 */
3710static void
3711setjobctl(int on)
3712{
3713 int fd;
3714 int pgrp;
3715
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003716 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003717 return;
3718 if (on) {
3719 int ofd;
3720 ofd = fd = open(_PATH_TTY, O_RDWR);
3721 if (fd < 0) {
3722 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3723 * That sometimes helps to acquire controlling tty.
3724 * Obviously, a workaround for bugs when someone
3725 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003726 fd = 2;
3727 while (!isatty(fd))
3728 if (--fd < 0)
3729 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003730 }
3731 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003732 if (ofd >= 0)
3733 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003734 if (fd < 0)
3735 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003736 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003737 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003738 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003739 pgrp = tcgetpgrp(fd);
3740 if (pgrp < 0) {
3741 out:
3742 ash_msg("can't access tty; job control turned off");
3743 mflag = on = 0;
3744 goto close;
3745 }
3746 if (pgrp == getpgrp())
3747 break;
3748 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003749 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003750 initialpgrp = pgrp;
3751
3752 setsignal(SIGTSTP);
3753 setsignal(SIGTTOU);
3754 setsignal(SIGTTIN);
3755 pgrp = rootpid;
3756 setpgid(0, pgrp);
3757 xtcsetpgrp(fd, pgrp);
3758 } else {
3759 /* turning job control off */
3760 fd = ttyfd;
3761 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003762 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003763 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003764 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765 setpgid(0, pgrp);
3766 setsignal(SIGTSTP);
3767 setsignal(SIGTTOU);
3768 setsignal(SIGTTIN);
3769 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003770 if (fd >= 0)
3771 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003772 fd = -1;
3773 }
3774 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003775 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003776}
3777
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003778static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779killcmd(int argc, char **argv)
3780{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003781 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003782 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003783 do {
3784 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003785 /*
3786 * "kill %N" - job kill
3787 * Converting to pgrp / pid kill
3788 */
3789 struct job *jp;
3790 char *dst;
3791 int j, n;
3792
3793 jp = getjob(argv[i], 0);
3794 /*
3795 * In jobs started under job control, we signal
3796 * entire process group by kill -PGRP_ID.
3797 * This happens, f.e., in interactive shell.
3798 *
3799 * Otherwise, we signal each child via
3800 * kill PID1 PID2 PID3.
3801 * Testcases:
3802 * sh -c 'sleep 1|sleep 1 & kill %1'
3803 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3804 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3805 */
3806 n = jp->nprocs; /* can't be 0 (I hope) */
3807 if (jp->jobctl)
3808 n = 1;
3809 dst = alloca(n * sizeof(int)*4);
3810 argv[i] = dst;
3811 for (j = 0; j < n; j++) {
3812 struct procstat *ps = &jp->ps[j];
3813 /* Skip non-running and not-stopped members
3814 * (i.e. dead members) of the job
3815 */
3816 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3817 continue;
3818 /*
3819 * kill_main has matching code to expect
3820 * leading space. Needed to not confuse
3821 * negative pids with "kill -SIGNAL_NO" syntax
3822 */
3823 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3824 }
3825 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003826 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003827 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003828 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003829 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003830}
3831
3832static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003833showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003834{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003835 struct procstat *ps;
3836 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003837
Denys Vlasenko285ad152009-12-04 23:02:27 +01003838 psend = jp->ps + jp->nprocs;
3839 for (ps = jp->ps + 1; ps < psend; ps++)
3840 printf(" | %s", ps->ps_cmd);
3841 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842 flush_stdout_stderr();
3843}
3844
3845
3846static int
3847restartjob(struct job *jp, int mode)
3848{
3849 struct procstat *ps;
3850 int i;
3851 int status;
3852 pid_t pgid;
3853
3854 INT_OFF;
3855 if (jp->state == JOBDONE)
3856 goto out;
3857 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003858 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003859 if (mode == FORK_FG)
3860 xtcsetpgrp(ttyfd, pgid);
3861 killpg(pgid, SIGCONT);
3862 ps = jp->ps;
3863 i = jp->nprocs;
3864 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003865 if (WIFSTOPPED(ps->ps_status)) {
3866 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003867 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003868 ps++;
3869 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003870 out:
3871 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3872 INT_ON;
3873 return status;
3874}
3875
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003876static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003877fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003878{
3879 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003880 int mode;
3881 int retval;
3882
3883 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3884 nextopt(nullstr);
3885 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003886 do {
3887 jp = getjob(*argv, 1);
3888 if (mode == FORK_BG) {
3889 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003890 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003891 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003892 out1str(jp->ps[0].ps_cmd);
3893 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003894 retval = restartjob(jp, mode);
3895 } while (*argv && *++argv);
3896 return retval;
3897}
3898#endif
3899
3900static int
3901sprint_status(char *s, int status, int sigonly)
3902{
3903 int col;
3904 int st;
3905
3906 col = 0;
3907 if (!WIFEXITED(status)) {
3908#if JOBS
3909 if (WIFSTOPPED(status))
3910 st = WSTOPSIG(status);
3911 else
3912#endif
3913 st = WTERMSIG(status);
3914 if (sigonly) {
3915 if (st == SIGINT || st == SIGPIPE)
3916 goto out;
3917#if JOBS
3918 if (WIFSTOPPED(status))
3919 goto out;
3920#endif
3921 }
3922 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003923//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003924 col = fmtstr(s, 32, strsignal(st));
3925 if (WCOREDUMP(status)) {
3926 col += fmtstr(s + col, 16, " (core dumped)");
3927 }
3928 } else if (!sigonly) {
3929 st = WEXITSTATUS(status);
3930 if (st)
3931 col = fmtstr(s, 16, "Done(%d)", st);
3932 else
3933 col = fmtstr(s, 16, "Done");
3934 }
3935 out:
3936 return col;
3937}
3938
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003939static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003940dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003941{
3942 int pid;
3943 int status;
3944 struct job *jp;
3945 struct job *thisjob;
3946 int state;
3947
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003948 TRACE(("dowait(0x%x) called\n", wait_flags));
3949
3950 /* Do a wait system call. If job control is compiled in, we accept
3951 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3952 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003953 if (doing_jobctl)
3954 wait_flags |= WUNTRACED;
3955 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003956 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3957 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003958 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003959 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003960
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003961 INT_OFF;
3962 thisjob = NULL;
3963 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003964 struct procstat *ps;
3965 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966 if (jp->state == JOBDONE)
3967 continue;
3968 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003969 ps = jp->ps;
3970 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003972 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003973 TRACE(("Job %d: changing status of proc %d "
3974 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003975 jobno(jp), pid, ps->ps_status, status));
3976 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003977 thisjob = jp;
3978 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003979 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003980 state = JOBRUNNING;
3981#if JOBS
3982 if (state == JOBRUNNING)
3983 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003984 if (WIFSTOPPED(ps->ps_status)) {
3985 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003986 state = JOBSTOPPED;
3987 }
3988#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003989 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003990 if (thisjob)
3991 goto gotjob;
3992 }
3993#if JOBS
3994 if (!WIFSTOPPED(status))
3995#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003996 jobless--;
3997 goto out;
3998
3999 gotjob:
4000 if (state != JOBRUNNING) {
4001 thisjob->changed = 1;
4002
4003 if (thisjob->state != state) {
4004 TRACE(("Job %d: changing state from %d to %d\n",
4005 jobno(thisjob), thisjob->state, state));
4006 thisjob->state = state;
4007#if JOBS
4008 if (state == JOBSTOPPED) {
4009 set_curjob(thisjob, CUR_STOPPED);
4010 }
4011#endif
4012 }
4013 }
4014
4015 out:
4016 INT_ON;
4017
4018 if (thisjob && thisjob == job) {
4019 char s[48 + 1];
4020 int len;
4021
4022 len = sprint_status(s, status, 1);
4023 if (len) {
4024 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004025 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004026 out2str(s);
4027 }
4028 }
4029 return pid;
4030}
4031
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004032static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004033blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004034{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004035 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004036 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004037 raise_exception(EXSIG);
4038 return pid;
4039}
4040
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041#if JOBS
4042static void
4043showjob(FILE *out, struct job *jp, int mode)
4044{
4045 struct procstat *ps;
4046 struct procstat *psend;
4047 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004048 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004049 char s[80];
4050
4051 ps = jp->ps;
4052
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004053 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004055 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004056 return;
4057 }
4058
4059 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004060 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004061
4062 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004063 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004065 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004066
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004067 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004068 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004069
4070 psend = ps + jp->nprocs;
4071
4072 if (jp->state == JOBRUNNING) {
4073 strcpy(s + col, "Running");
4074 col += sizeof("Running") - 1;
4075 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004076 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004077 if (jp->state == JOBSTOPPED)
4078 status = jp->stopstatus;
4079 col += sprint_status(s + col, status, 0);
4080 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004081 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004083 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4084 * or prints several "PID | <cmdN>" lines,
4085 * depending on SHOW_PIDS bit.
4086 * We do not print status of individual processes
4087 * between PID and <cmdN>. bash does it, but not very well:
4088 * first line shows overall job status, not process status,
4089 * making it impossible to know 1st process status.
4090 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004091 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004092 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004093 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004094 s[0] = '\0';
4095 col = 33;
4096 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004097 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004098 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004099 fprintf(out, "%s%*c%s%s",
4100 s,
4101 33 - col >= 0 ? 33 - col : 0, ' ',
4102 ps == jp->ps ? "" : "| ",
4103 ps->ps_cmd
4104 );
4105 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004106 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004107
4108 jp->changed = 0;
4109
4110 if (jp->state == JOBDONE) {
4111 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4112 freejob(jp);
4113 }
4114}
4115
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004116/*
4117 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4118 * statuses have changed since the last call to showjobs.
4119 */
4120static void
4121showjobs(FILE *out, int mode)
4122{
4123 struct job *jp;
4124
Denys Vlasenko883cea42009-07-11 15:31:59 +02004125 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004126
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004127 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004128 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004129 continue;
4130
4131 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004132 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004133 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004134 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004135 }
4136}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004137
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004138static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004139jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004140{
4141 int mode, m;
4142
4143 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004144 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004145 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004146 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004147 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004148 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004149 }
4150
4151 argv = argptr;
4152 if (*argv) {
4153 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004154 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004155 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004156 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004157 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004158 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004159
4160 return 0;
4161}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004162#endif /* JOBS */
4163
Michael Abbott359da5e2009-12-04 23:03:29 +01004164/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004165static int
4166getstatus(struct job *job)
4167{
4168 int status;
4169 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004170 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004171
Michael Abbott359da5e2009-12-04 23:03:29 +01004172 /* Fetch last member's status */
4173 ps = job->ps + job->nprocs - 1;
4174 status = ps->ps_status;
4175 if (pipefail) {
4176 /* "set -o pipefail" mode: use last _nonzero_ status */
4177 while (status == 0 && --ps >= job->ps)
4178 status = ps->ps_status;
4179 }
4180
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004181 retval = WEXITSTATUS(status);
4182 if (!WIFEXITED(status)) {
4183#if JOBS
4184 retval = WSTOPSIG(status);
4185 if (!WIFSTOPPED(status))
4186#endif
4187 {
4188 /* XXX: limits number of signals */
4189 retval = WTERMSIG(status);
4190#if JOBS
4191 if (retval == SIGINT)
4192 job->sigint = 1;
4193#endif
4194 }
4195 retval += 128;
4196 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004197 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004198 jobno(job), job->nprocs, status, retval));
4199 return retval;
4200}
4201
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004202static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004203waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204{
4205 struct job *job;
4206 int retval;
4207 struct job *jp;
4208
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004209 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004210 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004211
4212 nextopt(nullstr);
4213 retval = 0;
4214
4215 argv = argptr;
4216 if (!*argv) {
4217 /* wait for all jobs */
4218 for (;;) {
4219 jp = curjob;
4220 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004221 if (!jp) /* no running procs */
4222 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004223 if (jp->state == JOBRUNNING)
4224 break;
4225 jp->waited = 1;
4226 jp = jp->prev_job;
4227 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004228 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004229 /* man bash:
4230 * "When bash is waiting for an asynchronous command via
4231 * the wait builtin, the reception of a signal for which a trap
4232 * has been set will cause the wait builtin to return immediately
4233 * with an exit status greater than 128, immediately after which
4234 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004235 *
4236 * blocking_wait_with_raise_on_sig raises signal handlers
4237 * if it gets no pid (pid < 0). However,
4238 * if child sends us a signal *and immediately exits*,
4239 * blocking_wait_with_raise_on_sig gets pid > 0
4240 * and does not handle pending_sig. Check this case: */
4241 if (pending_sig)
4242 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004243 }
4244 }
4245
4246 retval = 127;
4247 do {
4248 if (**argv != '%') {
4249 pid_t pid = number(*argv);
4250 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004251 while (1) {
4252 if (!job)
4253 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004254 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004255 break;
4256 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004257 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004258 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004259 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004260 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004261 /* loop until process terminated or stopped */
4262 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004263 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264 job->waited = 1;
4265 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004266 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004267 } while (*++argv);
4268
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004269 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004270 return retval;
4271}
4272
4273static struct job *
4274growjobtab(void)
4275{
4276 size_t len;
4277 ptrdiff_t offset;
4278 struct job *jp, *jq;
4279
4280 len = njobs * sizeof(*jp);
4281 jq = jobtab;
4282 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4283
4284 offset = (char *)jp - (char *)jq;
4285 if (offset) {
4286 /* Relocate pointers */
4287 size_t l = len;
4288
4289 jq = (struct job *)((char *)jq + l);
4290 while (l) {
4291 l -= sizeof(*jp);
4292 jq--;
4293#define joff(p) ((struct job *)((char *)(p) + l))
4294#define jmove(p) (p) = (void *)((char *)(p) + offset)
4295 if (joff(jp)->ps == &jq->ps0)
4296 jmove(joff(jp)->ps);
4297 if (joff(jp)->prev_job)
4298 jmove(joff(jp)->prev_job);
4299 }
4300 if (curjob)
4301 jmove(curjob);
4302#undef joff
4303#undef jmove
4304 }
4305
4306 njobs += 4;
4307 jobtab = jp;
4308 jp = (struct job *)((char *)jp + len);
4309 jq = jp + 3;
4310 do {
4311 jq->used = 0;
4312 } while (--jq >= jp);
4313 return jp;
4314}
4315
4316/*
4317 * Return a new job structure.
4318 * Called with interrupts off.
4319 */
4320static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004321makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004322{
4323 int i;
4324 struct job *jp;
4325
4326 for (i = njobs, jp = jobtab; ; jp++) {
4327 if (--i < 0) {
4328 jp = growjobtab();
4329 break;
4330 }
4331 if (jp->used == 0)
4332 break;
4333 if (jp->state != JOBDONE || !jp->waited)
4334 continue;
4335#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004336 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004337 continue;
4338#endif
4339 freejob(jp);
4340 break;
4341 }
4342 memset(jp, 0, sizeof(*jp));
4343#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004344 /* jp->jobctl is a bitfield.
4345 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004346 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004347 jp->jobctl = 1;
4348#endif
4349 jp->prev_job = curjob;
4350 curjob = jp;
4351 jp->used = 1;
4352 jp->ps = &jp->ps0;
4353 if (nprocs > 1) {
4354 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4355 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004356 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004357 jobno(jp)));
4358 return jp;
4359}
4360
4361#if JOBS
4362/*
4363 * Return a string identifying a command (to be printed by the
4364 * jobs command).
4365 */
4366static char *cmdnextc;
4367
4368static void
4369cmdputs(const char *s)
4370{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004371 static const char vstype[VSTYPE + 1][3] = {
4372 "", "}", "-", "+", "?", "=",
4373 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004374 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004375 };
4376
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004377 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004378 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004379 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004380 unsigned char c;
4381 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004382 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004383
Denys Vlasenko46a14772009-12-10 21:27:13 +01004384 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004385 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4386 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004387 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004388 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004389 switch (c) {
4390 case CTLESC:
4391 c = *p++;
4392 break;
4393 case CTLVAR:
4394 subtype = *p++;
4395 if ((subtype & VSTYPE) == VSLENGTH)
4396 str = "${#";
4397 else
4398 str = "${";
4399 if (!(subtype & VSQUOTE) == !(quoted & 1))
4400 goto dostr;
4401 quoted ^= 1;
4402 c = '"';
4403 break;
4404 case CTLENDVAR:
4405 str = "\"}" + !(quoted & 1);
4406 quoted >>= 1;
4407 subtype = 0;
4408 goto dostr;
4409 case CTLBACKQ:
4410 str = "$(...)";
4411 goto dostr;
4412 case CTLBACKQ+CTLQUOTE:
4413 str = "\"$(...)\"";
4414 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004415#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004416 case CTLARI:
4417 str = "$((";
4418 goto dostr;
4419 case CTLENDARI:
4420 str = "))";
4421 goto dostr;
4422#endif
4423 case CTLQUOTEMARK:
4424 quoted ^= 1;
4425 c = '"';
4426 break;
4427 case '=':
4428 if (subtype == 0)
4429 break;
4430 if ((subtype & VSTYPE) != VSNORMAL)
4431 quoted <<= 1;
4432 str = vstype[subtype & VSTYPE];
4433 if (subtype & VSNUL)
4434 c = ':';
4435 else
4436 goto checkstr;
4437 break;
4438 case '\'':
4439 case '\\':
4440 case '"':
4441 case '$':
4442 /* These can only happen inside quotes */
4443 cc[0] = c;
4444 str = cc;
4445 c = '\\';
4446 break;
4447 default:
4448 break;
4449 }
4450 USTPUTC(c, nextc);
4451 checkstr:
4452 if (!str)
4453 continue;
4454 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004455 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004456 USTPUTC(c, nextc);
4457 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004458 } /* while *p++ not NUL */
4459
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004460 if (quoted & 1) {
4461 USTPUTC('"', nextc);
4462 }
4463 *nextc = 0;
4464 cmdnextc = nextc;
4465}
4466
4467/* cmdtxt() and cmdlist() call each other */
4468static void cmdtxt(union node *n);
4469
4470static void
4471cmdlist(union node *np, int sep)
4472{
4473 for (; np; np = np->narg.next) {
4474 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004475 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004476 cmdtxt(np);
4477 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004478 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004479 }
4480}
4481
4482static void
4483cmdtxt(union node *n)
4484{
4485 union node *np;
4486 struct nodelist *lp;
4487 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488
4489 if (!n)
4490 return;
4491 switch (n->type) {
4492 default:
4493#if DEBUG
4494 abort();
4495#endif
4496 case NPIPE:
4497 lp = n->npipe.cmdlist;
4498 for (;;) {
4499 cmdtxt(lp->n);
4500 lp = lp->next;
4501 if (!lp)
4502 break;
4503 cmdputs(" | ");
4504 }
4505 break;
4506 case NSEMI:
4507 p = "; ";
4508 goto binop;
4509 case NAND:
4510 p = " && ";
4511 goto binop;
4512 case NOR:
4513 p = " || ";
4514 binop:
4515 cmdtxt(n->nbinary.ch1);
4516 cmdputs(p);
4517 n = n->nbinary.ch2;
4518 goto donode;
4519 case NREDIR:
4520 case NBACKGND:
4521 n = n->nredir.n;
4522 goto donode;
4523 case NNOT:
4524 cmdputs("!");
4525 n = n->nnot.com;
4526 donode:
4527 cmdtxt(n);
4528 break;
4529 case NIF:
4530 cmdputs("if ");
4531 cmdtxt(n->nif.test);
4532 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004533 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004534 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004535 cmdputs("; else ");
4536 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004537 } else {
4538 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004539 }
4540 p = "; fi";
4541 goto dotail;
4542 case NSUBSHELL:
4543 cmdputs("(");
4544 n = n->nredir.n;
4545 p = ")";
4546 goto dotail;
4547 case NWHILE:
4548 p = "while ";
4549 goto until;
4550 case NUNTIL:
4551 p = "until ";
4552 until:
4553 cmdputs(p);
4554 cmdtxt(n->nbinary.ch1);
4555 n = n->nbinary.ch2;
4556 p = "; done";
4557 dodo:
4558 cmdputs("; do ");
4559 dotail:
4560 cmdtxt(n);
4561 goto dotail2;
4562 case NFOR:
4563 cmdputs("for ");
4564 cmdputs(n->nfor.var);
4565 cmdputs(" in ");
4566 cmdlist(n->nfor.args, 1);
4567 n = n->nfor.body;
4568 p = "; done";
4569 goto dodo;
4570 case NDEFUN:
4571 cmdputs(n->narg.text);
4572 p = "() { ... }";
4573 goto dotail2;
4574 case NCMD:
4575 cmdlist(n->ncmd.args, 1);
4576 cmdlist(n->ncmd.redirect, 0);
4577 break;
4578 case NARG:
4579 p = n->narg.text;
4580 dotail2:
4581 cmdputs(p);
4582 break;
4583 case NHERE:
4584 case NXHERE:
4585 p = "<<...";
4586 goto dotail2;
4587 case NCASE:
4588 cmdputs("case ");
4589 cmdputs(n->ncase.expr->narg.text);
4590 cmdputs(" in ");
4591 for (np = n->ncase.cases; np; np = np->nclist.next) {
4592 cmdtxt(np->nclist.pattern);
4593 cmdputs(") ");
4594 cmdtxt(np->nclist.body);
4595 cmdputs(";; ");
4596 }
4597 p = "esac";
4598 goto dotail2;
4599 case NTO:
4600 p = ">";
4601 goto redir;
4602 case NCLOBBER:
4603 p = ">|";
4604 goto redir;
4605 case NAPPEND:
4606 p = ">>";
4607 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004608#if ENABLE_ASH_BASH_COMPAT
4609 case NTO2:
4610#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004611 case NTOFD:
4612 p = ">&";
4613 goto redir;
4614 case NFROM:
4615 p = "<";
4616 goto redir;
4617 case NFROMFD:
4618 p = "<&";
4619 goto redir;
4620 case NFROMTO:
4621 p = "<>";
4622 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004623 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004624 cmdputs(p);
4625 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004626 cmdputs(utoa(n->ndup.dupfd));
4627 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004628 }
4629 n = n->nfile.fname;
4630 goto donode;
4631 }
4632}
4633
4634static char *
4635commandtext(union node *n)
4636{
4637 char *name;
4638
4639 STARTSTACKSTR(cmdnextc);
4640 cmdtxt(n);
4641 name = stackblock();
4642 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4643 name, cmdnextc, cmdnextc));
4644 return ckstrdup(name);
4645}
4646#endif /* JOBS */
4647
4648/*
4649 * Fork off a subshell. If we are doing job control, give the subshell its
4650 * own process group. Jp is a job structure that the job is to be added to.
4651 * N is the command that will be evaluated by the child. Both jp and n may
4652 * be NULL. The mode parameter can be one of the following:
4653 * FORK_FG - Fork off a foreground process.
4654 * FORK_BG - Fork off a background process.
4655 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4656 * process group even if job control is on.
4657 *
4658 * When job control is turned off, background processes have their standard
4659 * input redirected to /dev/null (except for the second and later processes
4660 * in a pipeline).
4661 *
4662 * Called with interrupts off.
4663 */
4664/*
4665 * Clear traps on a fork.
4666 */
4667static void
4668clear_traps(void)
4669{
4670 char **tp;
4671
4672 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004673 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004674 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004675 if (trap_ptr == trap)
4676 free(*tp);
4677 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004678 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004679 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004680 setsignal(tp - trap);
4681 INT_ON;
4682 }
4683 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004684 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004685}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004686
4687/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004688static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004689
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004690/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004691static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004692forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004693{
4694 int oldlvl;
4695
4696 TRACE(("Child shell %d\n", getpid()));
4697 oldlvl = shlvl;
4698 shlvl++;
4699
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004700 /* man bash: "Non-builtin commands run by bash have signal handlers
4701 * set to the values inherited by the shell from its parent".
4702 * Do we do it correctly? */
4703
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004704 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004705
4706 if (mode == FORK_NOJOB /* is it `xxx` ? */
4707 && n && n->type == NCMD /* is it single cmd? */
4708 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004709 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004710 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4711 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4712 ) {
4713 TRACE(("Trap hack\n"));
4714 /* Awful hack for `trap` or $(trap).
4715 *
4716 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4717 * contains an example where "trap" is executed in a subshell:
4718 *
4719 * save_traps=$(trap)
4720 * ...
4721 * eval "$save_traps"
4722 *
4723 * Standard does not say that "trap" in subshell shall print
4724 * parent shell's traps. It only says that its output
4725 * must have suitable form, but then, in the above example
4726 * (which is not supposed to be normative), it implies that.
4727 *
4728 * bash (and probably other shell) does implement it
4729 * (traps are reset to defaults, but "trap" still shows them),
4730 * but as a result, "trap" logic is hopelessly messed up:
4731 *
4732 * # trap
4733 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4734 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4735 * # true | trap <--- trap is in subshell - no output (ditto)
4736 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4737 * trap -- 'echo Ho' SIGWINCH
4738 * # echo `(trap)` <--- in subshell in subshell - output
4739 * trap -- 'echo Ho' SIGWINCH
4740 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4741 * trap -- 'echo Ho' SIGWINCH
4742 *
4743 * The rules when to forget and when to not forget traps
4744 * get really complex and nonsensical.
4745 *
4746 * Our solution: ONLY bare $(trap) or `trap` is special.
4747 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004748 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004749 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004750 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004751 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004752 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004753#if JOBS
4754 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004755 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004756 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004757 pid_t pgrp;
4758
4759 if (jp->nprocs == 0)
4760 pgrp = getpid();
4761 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004762 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004763 /* this can fail because we are doing it in the parent also */
4764 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004765 if (mode == FORK_FG)
4766 xtcsetpgrp(ttyfd, pgrp);
4767 setsignal(SIGTSTP);
4768 setsignal(SIGTTOU);
4769 } else
4770#endif
4771 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004772 /* man bash: "When job control is not in effect,
4773 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004774 ignoresig(SIGINT);
4775 ignoresig(SIGQUIT);
4776 if (jp->nprocs == 0) {
4777 close(0);
4778 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004779 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004780 }
4781 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004782 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004783 if (iflag) { /* why if iflag only? */
4784 setsignal(SIGINT);
4785 setsignal(SIGTERM);
4786 }
4787 /* man bash:
4788 * "In all cases, bash ignores SIGQUIT. Non-builtin
4789 * commands run by bash have signal handlers
4790 * set to the values inherited by the shell
4791 * from its parent".
4792 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004793 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004794 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004795#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004796 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004797 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004798 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004799 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004800 /* "jobs": we do not want to clear job list for it,
4801 * instead we remove only _its_ own_ job from job list.
4802 * This makes "jobs .... | cat" more useful.
4803 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004804 freejob(curjob);
4805 return;
4806 }
4807#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004808 for (jp = curjob; jp; jp = jp->prev_job)
4809 freejob(jp);
4810 jobless = 0;
4811}
4812
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004813/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004814#if !JOBS
4815#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4816#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004817static void
4818forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4819{
4820 TRACE(("In parent shell: child = %d\n", pid));
4821 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004822 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4823 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004824 jobless++;
4825 return;
4826 }
4827#if JOBS
4828 if (mode != FORK_NOJOB && jp->jobctl) {
4829 int pgrp;
4830
4831 if (jp->nprocs == 0)
4832 pgrp = pid;
4833 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004834 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004835 /* This can fail because we are doing it in the child also */
4836 setpgid(pid, pgrp);
4837 }
4838#endif
4839 if (mode == FORK_BG) {
4840 backgndpid = pid; /* set $! */
4841 set_curjob(jp, CUR_RUNNING);
4842 }
4843 if (jp) {
4844 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004845 ps->ps_pid = pid;
4846 ps->ps_status = -1;
4847 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004848#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004849 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004850 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004851#endif
4852 }
4853}
4854
4855static int
4856forkshell(struct job *jp, union node *n, int mode)
4857{
4858 int pid;
4859
4860 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4861 pid = fork();
4862 if (pid < 0) {
4863 TRACE(("Fork failed, errno=%d", errno));
4864 if (jp)
4865 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004866 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004867 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004868 if (pid == 0) {
4869 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004870 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004871 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004872 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004873 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004874 return pid;
4875}
4876
4877/*
4878 * Wait for job to finish.
4879 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004880 * Under job control we have the problem that while a child process
4881 * is running interrupts generated by the user are sent to the child
4882 * but not to the shell. This means that an infinite loop started by
4883 * an interactive user may be hard to kill. With job control turned off,
4884 * an interactive user may place an interactive program inside a loop.
4885 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004886 * these interrupts to also abort the loop. The approach we take here
4887 * is to have the shell ignore interrupt signals while waiting for a
4888 * foreground process to terminate, and then send itself an interrupt
4889 * signal if the child process was terminated by an interrupt signal.
4890 * Unfortunately, some programs want to do a bit of cleanup and then
4891 * exit on interrupt; unless these processes terminate themselves by
4892 * sending a signal to themselves (instead of calling exit) they will
4893 * confuse this approach.
4894 *
4895 * Called with interrupts off.
4896 */
4897static int
4898waitforjob(struct job *jp)
4899{
4900 int st;
4901
4902 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004903
4904 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004905 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004906 /* In non-interactive shells, we _can_ get
4907 * a keyboard signal here and be EINTRed,
4908 * but we just loop back, waiting for command to complete.
4909 *
4910 * man bash:
4911 * "If bash is waiting for a command to complete and receives
4912 * a signal for which a trap has been set, the trap
4913 * will not be executed until the command completes."
4914 *
4915 * Reality is that even if trap is not set, bash
4916 * will not act on the signal until command completes.
4917 * Try this. sleep5intoff.c:
4918 * #include <signal.h>
4919 * #include <unistd.h>
4920 * int main() {
4921 * sigset_t set;
4922 * sigemptyset(&set);
4923 * sigaddset(&set, SIGINT);
4924 * sigaddset(&set, SIGQUIT);
4925 * sigprocmask(SIG_BLOCK, &set, NULL);
4926 * sleep(5);
4927 * return 0;
4928 * }
4929 * $ bash -c './sleep5intoff; echo hi'
4930 * ^C^C^C^C <--- pressing ^C once a second
4931 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004932 * $ bash -c './sleep5intoff; echo hi'
4933 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4934 * $ _
4935 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004936 dowait(DOWAIT_BLOCK, jp);
4937 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004938 INT_ON;
4939
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004940 st = getstatus(jp);
4941#if JOBS
4942 if (jp->jobctl) {
4943 xtcsetpgrp(ttyfd, rootpid);
4944 /*
4945 * This is truly gross.
4946 * If we're doing job control, then we did a TIOCSPGRP which
4947 * caused us (the shell) to no longer be in the controlling
4948 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4949 * intuit from the subprocess exit status whether a SIGINT
4950 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4951 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004952 if (jp->sigint) /* TODO: do the same with all signals */
4953 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004954 }
4955 if (jp->state == JOBDONE)
4956#endif
4957 freejob(jp);
4958 return st;
4959}
4960
4961/*
4962 * return 1 if there are stopped jobs, otherwise 0
4963 */
4964static int
4965stoppedjobs(void)
4966{
4967 struct job *jp;
4968 int retval;
4969
4970 retval = 0;
4971 if (job_warning)
4972 goto out;
4973 jp = curjob;
4974 if (jp && jp->state == JOBSTOPPED) {
4975 out2str("You have stopped jobs.\n");
4976 job_warning = 2;
4977 retval++;
4978 }
4979 out:
4980 return retval;
4981}
4982
4983
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004984/* ============ redir.c
4985 *
4986 * Code for dealing with input/output redirection.
4987 */
4988
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004989#undef EMPTY
4990#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004991#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004992#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004993
4994/*
4995 * Open a file in noclobber mode.
4996 * The code was copied from bash.
4997 */
4998static int
4999noclobberopen(const char *fname)
5000{
5001 int r, fd;
5002 struct stat finfo, finfo2;
5003
5004 /*
5005 * If the file exists and is a regular file, return an error
5006 * immediately.
5007 */
5008 r = stat(fname, &finfo);
5009 if (r == 0 && S_ISREG(finfo.st_mode)) {
5010 errno = EEXIST;
5011 return -1;
5012 }
5013
5014 /*
5015 * If the file was not present (r != 0), make sure we open it
5016 * exclusively so that if it is created before we open it, our open
5017 * will fail. Make sure that we do not truncate an existing file.
5018 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5019 * file was not a regular file, we leave O_EXCL off.
5020 */
5021 if (r != 0)
5022 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5023 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5024
5025 /* If the open failed, return the file descriptor right away. */
5026 if (fd < 0)
5027 return fd;
5028
5029 /*
5030 * OK, the open succeeded, but the file may have been changed from a
5031 * non-regular file to a regular file between the stat and the open.
5032 * We are assuming that the O_EXCL open handles the case where FILENAME
5033 * did not exist and is symlinked to an existing file between the stat
5034 * and open.
5035 */
5036
5037 /*
5038 * If we can open it and fstat the file descriptor, and neither check
5039 * revealed that it was a regular file, and the file has not been
5040 * replaced, return the file descriptor.
5041 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005042 if (fstat(fd, &finfo2) == 0
5043 && !S_ISREG(finfo2.st_mode)
5044 && finfo.st_dev == finfo2.st_dev
5045 && finfo.st_ino == finfo2.st_ino
5046 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005047 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005048 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005049
5050 /* The file has been replaced. badness. */
5051 close(fd);
5052 errno = EEXIST;
5053 return -1;
5054}
5055
5056/*
5057 * Handle here documents. Normally we fork off a process to write the
5058 * data to a pipe. If the document is short, we can stuff the data in
5059 * the pipe without forking.
5060 */
5061/* openhere needs this forward reference */
5062static void expandhere(union node *arg, int fd);
5063static int
5064openhere(union node *redir)
5065{
5066 int pip[2];
5067 size_t len = 0;
5068
5069 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005070 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005071 if (redir->type == NHERE) {
5072 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005073 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005074 full_write(pip[1], redir->nhere.doc->narg.text, len);
5075 goto out;
5076 }
5077 }
5078 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005079 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005080 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005081 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5082 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5083 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5084 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005085 signal(SIGPIPE, SIG_DFL);
5086 if (redir->type == NHERE)
5087 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005088 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005089 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005090 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005091 }
5092 out:
5093 close(pip[1]);
5094 return pip[0];
5095}
5096
5097static int
5098openredirect(union node *redir)
5099{
5100 char *fname;
5101 int f;
5102
5103 switch (redir->nfile.type) {
5104 case NFROM:
5105 fname = redir->nfile.expfname;
5106 f = open(fname, O_RDONLY);
5107 if (f < 0)
5108 goto eopen;
5109 break;
5110 case NFROMTO:
5111 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005112 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005113 if (f < 0)
5114 goto ecreate;
5115 break;
5116 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005117#if ENABLE_ASH_BASH_COMPAT
5118 case NTO2:
5119#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005120 /* Take care of noclobber mode. */
5121 if (Cflag) {
5122 fname = redir->nfile.expfname;
5123 f = noclobberopen(fname);
5124 if (f < 0)
5125 goto ecreate;
5126 break;
5127 }
5128 /* FALLTHROUGH */
5129 case NCLOBBER:
5130 fname = redir->nfile.expfname;
5131 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5132 if (f < 0)
5133 goto ecreate;
5134 break;
5135 case NAPPEND:
5136 fname = redir->nfile.expfname;
5137 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5138 if (f < 0)
5139 goto ecreate;
5140 break;
5141 default:
5142#if DEBUG
5143 abort();
5144#endif
5145 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005146/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005147// case NTOFD:
5148// case NFROMFD:
5149// f = -1;
5150// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005151 case NHERE:
5152 case NXHERE:
5153 f = openhere(redir);
5154 break;
5155 }
5156
5157 return f;
5158 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005159 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005161 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005162}
5163
5164/*
5165 * Copy a file descriptor to be >= to. Returns -1
5166 * if the source file descriptor is closed, EMPTY if there are no unused
5167 * file descriptors left.
5168 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005169/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5170 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005171enum {
5172 COPYFD_EXACT = (int)~(INT_MAX),
5173 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5174};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005175static int
5176copyfd(int from, int to)
5177{
5178 int newfd;
5179
Denis Vlasenko5a867312008-07-24 19:46:38 +00005180 if (to & COPYFD_EXACT) {
5181 to &= ~COPYFD_EXACT;
5182 /*if (from != to)*/
5183 newfd = dup2(from, to);
5184 } else {
5185 newfd = fcntl(from, F_DUPFD, to);
5186 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005187 if (newfd < 0) {
5188 if (errno == EMFILE)
5189 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005190 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005191 ash_msg_and_raise_error("%d: %m", from);
5192 }
5193 return newfd;
5194}
5195
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005196/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005197struct two_fd_t {
5198 int orig, copy;
5199};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005200struct redirtab {
5201 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005202 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005203 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005204 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005205};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005206#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005207
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005208static int need_to_remember(struct redirtab *rp, int fd)
5209{
5210 int i;
5211
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005212 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005213 return 0;
5214
5215 for (i = 0; i < rp->pair_count; i++) {
5216 if (rp->two_fd[i].orig == fd) {
5217 /* already remembered */
5218 return 0;
5219 }
5220 }
5221 return 1;
5222}
5223
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005224/* "hidden" fd is a fd used to read scripts, or a copy of such */
5225static int is_hidden_fd(struct redirtab *rp, int fd)
5226{
5227 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005228 struct parsefile *pf;
5229
5230 if (fd == -1)
5231 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005232 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005233 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005234 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005235 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005236 * $ ash # running ash interactively
5237 * $ . ./script.sh
5238 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005239 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005240 * it's still ok to use it: "read" builtin uses it,
5241 * why should we cripple "exec" builtin?
5242 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005243 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005244 return 1;
5245 }
5246 pf = pf->prev;
5247 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005248
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005249 if (!rp)
5250 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005251 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005252 fd |= COPYFD_RESTORE;
5253 for (i = 0; i < rp->pair_count; i++) {
5254 if (rp->two_fd[i].copy == fd) {
5255 return 1;
5256 }
5257 }
5258 return 0;
5259}
5260
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005261/*
5262 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5263 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005264 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005265 */
5266/* flags passed to redirect */
5267#define REDIR_PUSH 01 /* save previous values of file descriptors */
5268#define REDIR_SAVEFD2 03 /* set preverrout */
5269static void
5270redirect(union node *redir, int flags)
5271{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005272 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005273 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005274 int i;
5275 int fd;
5276 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005277 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005278
Denis Vlasenko01631112007-12-16 17:20:38 +00005279 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005280 if (!redir) {
5281 return;
5282 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005283
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005284 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005285 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005286 INT_OFF;
5287 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005288 union node *tmp = redir;
5289 do {
5290 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005291#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005292 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005293 sv_pos++;
5294#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005295 tmp = tmp->nfile.next;
5296 } while (tmp);
5297 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005298 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005299 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005300 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005301 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005302 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005303 while (sv_pos > 0) {
5304 sv_pos--;
5305 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5306 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005307 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005308
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005309 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005310 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005311 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005312 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005313 right_fd = redir->ndup.dupfd;
5314 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005315 /* redirect from/to same file descriptor? */
5316 if (right_fd == fd)
5317 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005318 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005319 if (is_hidden_fd(sv, right_fd)) {
5320 errno = EBADF; /* as if it is closed */
5321 ash_msg_and_raise_error("%d: %m", right_fd);
5322 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005323 newfd = -1;
5324 } else {
5325 newfd = openredirect(redir); /* always >= 0 */
5326 if (fd == newfd) {
5327 /* Descriptor wasn't open before redirect.
5328 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005329 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005330 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005331 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005332 continue;
5333 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005334 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005335#if ENABLE_ASH_BASH_COMPAT
5336 redirect_more:
5337#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005338 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005339 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005340 /* Careful to not accidentally "save"
5341 * to the same fd as right side fd in N>&M */
5342 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5343 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005344/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5345 * are closed in popredir() in the child, preventing them from leaking
5346 * into child. (popredir() also cleans up the mess in case of failures)
5347 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005348 if (i == -1) {
5349 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005350 if (i != EBADF) {
5351 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005352 if (newfd >= 0)
5353 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005354 errno = i;
5355 ash_msg_and_raise_error("%d: %m", fd);
5356 /* NOTREACHED */
5357 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005358 /* EBADF: it is not open - good, remember to close it */
5359 remember_to_close:
5360 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005361 } else { /* fd is open, save its copy */
5362 /* "exec fd>&-" should not close fds
5363 * which point to script file(s).
5364 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005365 if (is_hidden_fd(sv, fd))
5366 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005367 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005368 if (fd == 2)
5369 copied_fd2 = i;
5370 sv->two_fd[sv_pos].orig = fd;
5371 sv->two_fd[sv_pos].copy = i;
5372 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005373 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005374 if (newfd < 0) {
5375 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005376 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005377 /* Don't want to trigger debugging */
5378 if (fd != -1)
5379 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005380 } else {
5381 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005382 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005383 } else if (fd != newfd) { /* move newfd to fd */
5384 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005385#if ENABLE_ASH_BASH_COMPAT
5386 if (!(redir->nfile.type == NTO2 && fd == 2))
5387#endif
5388 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005389 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005390#if ENABLE_ASH_BASH_COMPAT
5391 if (redir->nfile.type == NTO2 && fd == 1) {
5392 /* We already redirected it to fd 1, now copy it to 2 */
5393 newfd = 1;
5394 fd = 2;
5395 goto redirect_more;
5396 }
5397#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005398 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005399
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005400 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005401 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5402 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005403}
5404
5405/*
5406 * Undo the effects of the last redirection.
5407 */
5408static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005409popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005410{
5411 struct redirtab *rp;
5412 int i;
5413
Denis Vlasenko01631112007-12-16 17:20:38 +00005414 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005415 return;
5416 INT_OFF;
5417 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005418 for (i = 0; i < rp->pair_count; i++) {
5419 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005420 int copy = rp->two_fd[i].copy;
5421 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005422 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005423 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005424 continue;
5425 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005426 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005427 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005428 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005429 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005430 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005431 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005432 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005433 }
5434 }
5435 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005436 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005437 free(rp);
5438 INT_ON;
5439}
5440
5441/*
5442 * Undo all redirections. Called on error or interrupt.
5443 */
5444
5445/*
5446 * Discard all saved file descriptors.
5447 */
5448static void
5449clearredir(int drop)
5450{
5451 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005452 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005453 if (!redirlist)
5454 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005455 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005456 }
5457}
5458
5459static int
5460redirectsafe(union node *redir, int flags)
5461{
5462 int err;
5463 volatile int saveint;
5464 struct jmploc *volatile savehandler = exception_handler;
5465 struct jmploc jmploc;
5466
5467 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005468 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5469 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005470 if (!err) {
5471 exception_handler = &jmploc;
5472 redirect(redir, flags);
5473 }
5474 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005475 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005476 longjmp(exception_handler->loc, 1);
5477 RESTORE_INT(saveint);
5478 return err;
5479}
5480
5481
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005482/* ============ Routines to expand arguments to commands
5483 *
5484 * We have to deal with backquotes, shell variables, and file metacharacters.
5485 */
5486
Mike Frysinger98c52642009-04-02 10:02:37 +00005487#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005488static arith_t
5489ash_arith(const char *s)
5490{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005491 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005492 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005493
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005494 math_state.lookupvar = lookupvar;
5495 math_state.setvar = setvar2;
5496 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005497
5498 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005499 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005500 if (math_state.errmsg)
5501 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005502 INT_ON;
5503
5504 return result;
5505}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005506#endif
5507
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005508/*
5509 * expandarg flags
5510 */
5511#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5512#define EXP_TILDE 0x2 /* do normal tilde expansion */
5513#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5514#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5515#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5516#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5517#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5518#define EXP_WORD 0x80 /* expand word in parameter expansion */
5519#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5520/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005521 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005522 */
5523#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5524#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5525#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5526#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5527#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5528
5529/*
5530 * Structure specifying which parts of the string should be searched
5531 * for IFS characters.
5532 */
5533struct ifsregion {
5534 struct ifsregion *next; /* next region in list */
5535 int begoff; /* offset of start of region */
5536 int endoff; /* offset of end of region */
5537 int nulonly; /* search for nul bytes only */
5538};
5539
5540struct arglist {
5541 struct strlist *list;
5542 struct strlist **lastp;
5543};
5544
5545/* output of current string */
5546static char *expdest;
5547/* list of back quote expressions */
5548static struct nodelist *argbackq;
5549/* first struct in list of ifs regions */
5550static struct ifsregion ifsfirst;
5551/* last struct in list */
5552static struct ifsregion *ifslastp;
5553/* holds expanded arg list */
5554static struct arglist exparg;
5555
5556/*
5557 * Our own itoa().
5558 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005559#if !ENABLE_SH_MATH_SUPPORT
5560/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5561typedef long arith_t;
5562# define ARITH_FMT "%ld"
5563#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005564static int
5565cvtnum(arith_t num)
5566{
5567 int len;
5568
5569 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005570 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005571 STADJUST(len, expdest);
5572 return len;
5573}
5574
5575static size_t
5576esclen(const char *start, const char *p)
5577{
5578 size_t esc = 0;
5579
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005580 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005581 esc++;
5582 }
5583 return esc;
5584}
5585
5586/*
5587 * Remove any CTLESC characters from a string.
5588 */
5589static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005590rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005591{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005592 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005593
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005594 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005595 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005596 unsigned protect_against_glob;
5597 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005598
5599 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005600 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005601 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005602
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005603 q = p;
5604 r = str;
5605 if (flag & RMESCAPE_ALLOC) {
5606 size_t len = p - str;
5607 size_t fulllen = len + strlen(p) + 1;
5608
5609 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005610 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005611 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005612 /* p and str may be invalidated by makestrspace */
5613 str = (char *)stackblock() + strloc;
5614 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005615 } else if (flag & RMESCAPE_HEAP) {
5616 r = ckmalloc(fulllen);
5617 } else {
5618 r = stalloc(fulllen);
5619 }
5620 q = r;
5621 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005622 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005623 }
5624 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005625
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005626 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5627 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005628 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005629 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005630 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005631// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5632// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5633// Note: both inquotes and protect_against_glob only affect whether
5634// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005635 inquotes = ~inquotes;
5636 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005637 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005638 continue;
5639 }
5640 if (*p == '\\') {
5641 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005642 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005643 goto copy;
5644 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005645 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005646 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005647 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648 *q++ = '\\';
5649 }
5650 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005651 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005652 copy:
5653 *q++ = *p++;
5654 }
5655 *q = '\0';
5656 if (flag & RMESCAPE_GROW) {
5657 expdest = r;
5658 STADJUST(q - r + 1, expdest);
5659 }
5660 return r;
5661}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005662#define pmatch(a, b) !fnmatch((a), (b), 0)
5663
5664/*
5665 * Prepare a pattern for a expmeta (internal glob(3)) call.
5666 *
5667 * Returns an stalloced string.
5668 */
5669static char *
5670preglob(const char *pattern, int quoted, int flag)
5671{
5672 flag |= RMESCAPE_GLOB;
5673 if (quoted) {
5674 flag |= RMESCAPE_QUOTED;
5675 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005676 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005677}
5678
5679/*
5680 * Put a string on the stack.
5681 */
5682static void
5683memtodest(const char *p, size_t len, int syntax, int quotes)
5684{
5685 char *q = expdest;
5686
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005687 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005688
5689 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005690 unsigned char c = *p++;
5691 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005692 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005693 if (quotes) {
5694 int n = SIT(c, syntax);
5695 if (n == CCTL || n == CBACK)
5696 USTPUTC(CTLESC, q);
5697 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005698 USTPUTC(c, q);
5699 }
5700
5701 expdest = q;
5702}
5703
5704static void
5705strtodest(const char *p, int syntax, int quotes)
5706{
5707 memtodest(p, strlen(p), syntax, quotes);
5708}
5709
5710/*
5711 * Record the fact that we have to scan this region of the
5712 * string for IFS characters.
5713 */
5714static void
5715recordregion(int start, int end, int nulonly)
5716{
5717 struct ifsregion *ifsp;
5718
5719 if (ifslastp == NULL) {
5720 ifsp = &ifsfirst;
5721 } else {
5722 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005723 ifsp = ckzalloc(sizeof(*ifsp));
5724 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005725 ifslastp->next = ifsp;
5726 INT_ON;
5727 }
5728 ifslastp = ifsp;
5729 ifslastp->begoff = start;
5730 ifslastp->endoff = end;
5731 ifslastp->nulonly = nulonly;
5732}
5733
5734static void
5735removerecordregions(int endoff)
5736{
5737 if (ifslastp == NULL)
5738 return;
5739
5740 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005741 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005742 struct ifsregion *ifsp;
5743 INT_OFF;
5744 ifsp = ifsfirst.next->next;
5745 free(ifsfirst.next);
5746 ifsfirst.next = ifsp;
5747 INT_ON;
5748 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005749 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005750 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005751 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005752 ifslastp = &ifsfirst;
5753 ifsfirst.endoff = endoff;
5754 }
5755 return;
5756 }
5757
5758 ifslastp = &ifsfirst;
5759 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005760 ifslastp = ifslastp->next;
5761 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005762 struct ifsregion *ifsp;
5763 INT_OFF;
5764 ifsp = ifslastp->next->next;
5765 free(ifslastp->next);
5766 ifslastp->next = ifsp;
5767 INT_ON;
5768 }
5769 if (ifslastp->endoff > endoff)
5770 ifslastp->endoff = endoff;
5771}
5772
5773static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005774exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005775{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005776 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005777 char *name;
5778 struct passwd *pw;
5779 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005780 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005781 int startloc;
5782
5783 name = p + 1;
5784
5785 while ((c = *++p) != '\0') {
5786 switch (c) {
5787 case CTLESC:
5788 return startp;
5789 case CTLQUOTEMARK:
5790 return startp;
5791 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005792 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005793 goto done;
5794 break;
5795 case '/':
5796 case CTLENDVAR:
5797 goto done;
5798 }
5799 }
5800 done:
5801 *p = '\0';
5802 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005803 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005804 } else {
5805 pw = getpwnam(name);
5806 if (pw == NULL)
5807 goto lose;
5808 home = pw->pw_dir;
5809 }
5810 if (!home || !*home)
5811 goto lose;
5812 *p = c;
5813 startloc = expdest - (char *)stackblock();
5814 strtodest(home, SQSYNTAX, quotes);
5815 recordregion(startloc, expdest - (char *)stackblock(), 0);
5816 return p;
5817 lose:
5818 *p = c;
5819 return startp;
5820}
5821
5822/*
5823 * Execute a command inside back quotes. If it's a builtin command, we
5824 * want to save its output in a block obtained from malloc. Otherwise
5825 * we fork off a subprocess and get the output of the command via a pipe.
5826 * Should be called with interrupts off.
5827 */
5828struct backcmd { /* result of evalbackcmd */
5829 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005830 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005831 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005832 struct job *jp; /* job structure for command */
5833};
5834
5835/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005836static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005837#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005838static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005839
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005840static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005841evalbackcmd(union node *n, struct backcmd *result)
5842{
5843 int saveherefd;
5844
5845 result->fd = -1;
5846 result->buf = NULL;
5847 result->nleft = 0;
5848 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005849 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005850 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005851
5852 saveherefd = herefd;
5853 herefd = -1;
5854
5855 {
5856 int pip[2];
5857 struct job *jp;
5858
5859 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005860 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005861 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005862 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5863 FORCE_INT_ON;
5864 close(pip[0]);
5865 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005866 /*close(1);*/
5867 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 close(pip[1]);
5869 }
5870 eflag = 0;
5871 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5872 /* NOTREACHED */
5873 }
5874 close(pip[1]);
5875 result->fd = pip[0];
5876 result->jp = jp;
5877 }
5878 herefd = saveherefd;
5879 out:
5880 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5881 result->fd, result->buf, result->nleft, result->jp));
5882}
5883
5884/*
5885 * Expand stuff in backwards quotes.
5886 */
5887static void
5888expbackq(union node *cmd, int quoted, int quotes)
5889{
5890 struct backcmd in;
5891 int i;
5892 char buf[128];
5893 char *p;
5894 char *dest;
5895 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005896 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005897 struct stackmark smark;
5898
5899 INT_OFF;
5900 setstackmark(&smark);
5901 dest = expdest;
5902 startloc = dest - (char *)stackblock();
5903 grabstackstr(dest);
5904 evalbackcmd(cmd, &in);
5905 popstackmark(&smark);
5906
5907 p = in.buf;
5908 i = in.nleft;
5909 if (i == 0)
5910 goto read;
5911 for (;;) {
5912 memtodest(p, i, syntax, quotes);
5913 read:
5914 if (in.fd < 0)
5915 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005916 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005917 TRACE(("expbackq: read returns %d\n", i));
5918 if (i <= 0)
5919 break;
5920 p = buf;
5921 }
5922
Denis Vlasenko60818682007-09-28 22:07:23 +00005923 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005924 if (in.fd >= 0) {
5925 close(in.fd);
5926 back_exitstatus = waitforjob(in.jp);
5927 }
5928 INT_ON;
5929
5930 /* Eat all trailing newlines */
5931 dest = expdest;
5932 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5933 STUNPUTC(dest);
5934 expdest = dest;
5935
5936 if (quoted == 0)
5937 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005938 TRACE(("evalbackq: size:%d:'%.*s'\n",
5939 (int)((dest - (char *)stackblock()) - startloc),
5940 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005941 stackblock() + startloc));
5942}
5943
Mike Frysinger98c52642009-04-02 10:02:37 +00005944#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005945/*
5946 * Expand arithmetic expression. Backup to start of expression,
5947 * evaluate, place result in (backed up) result, adjust string position.
5948 */
5949static void
5950expari(int quotes)
5951{
5952 char *p, *start;
5953 int begoff;
5954 int flag;
5955 int len;
5956
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005957 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958
5959 /*
5960 * This routine is slightly over-complicated for
5961 * efficiency. Next we scan backwards looking for the
5962 * start of arithmetic.
5963 */
5964 start = stackblock();
5965 p = expdest - 1;
5966 *p = '\0';
5967 p--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005968 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005969 int esc;
5970
Denys Vlasenkocd716832009-11-28 22:14:02 +01005971 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005972 p--;
5973#if DEBUG
5974 if (p < start) {
5975 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5976 }
5977#endif
5978 }
5979
5980 esc = esclen(start, p);
5981 if (!(esc % 2)) {
5982 break;
5983 }
5984
5985 p -= esc + 1;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005986 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005987
5988 begoff = p - start;
5989
5990 removerecordregions(begoff);
5991
5992 flag = p[1];
5993
5994 expdest = p;
5995
5996 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005997 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005998
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005999 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006000
6001 if (flag != '"')
6002 recordregion(begoff, begoff + len, 0);
6003}
6004#endif
6005
6006/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006007static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006008
6009/*
6010 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6011 * characters to allow for further processing. Otherwise treat
6012 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006013 *
6014 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6015 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6016 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006017 */
6018static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006019argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006020{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006021 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006022 '=',
6023 ':',
6024 CTLQUOTEMARK,
6025 CTLENDVAR,
6026 CTLESC,
6027 CTLVAR,
6028 CTLBACKQ,
6029 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006030#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006031 CTLENDARI,
6032#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006033 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006034 };
6035 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006036 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6037 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006038 int inquotes;
6039 size_t length;
6040 int startloc;
6041
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006042 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006044 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045 reject++;
6046 }
6047 inquotes = 0;
6048 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006049 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006050 char *q;
6051
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006052 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006053 tilde:
6054 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006055 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006056 q++;
6057 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006058 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006059 }
6060 start:
6061 startloc = expdest - (char *)stackblock();
6062 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006063 unsigned char c;
6064
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006065 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006066 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006067 if (c) {
6068 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006069 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006070 ) {
6071 /* c == '=' || c == ':' || c == CTLENDARI */
6072 length++;
6073 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006074 }
6075 if (length > 0) {
6076 int newloc;
6077 expdest = stack_nputstr(p, length, expdest);
6078 newloc = expdest - (char *)stackblock();
6079 if (breakall && !inquotes && newloc > startloc) {
6080 recordregion(startloc, newloc, 0);
6081 }
6082 startloc = newloc;
6083 }
6084 p += length + 1;
6085 length = 0;
6086
6087 switch (c) {
6088 case '\0':
6089 goto breakloop;
6090 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006091 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006092 p--;
6093 continue;
6094 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006095 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006096 reject++;
6097 /* fall through */
6098 case ':':
6099 /*
6100 * sort of a hack - expand tildes in variable
6101 * assignments (after the first '=' and after ':'s).
6102 */
6103 if (*--p == '~') {
6104 goto tilde;
6105 }
6106 continue;
6107 }
6108
6109 switch (c) {
6110 case CTLENDVAR: /* ??? */
6111 goto breakloop;
6112 case CTLQUOTEMARK:
6113 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006114 if (!inquotes
6115 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006116 && ( p[4] == (char)CTLQUOTEMARK
6117 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006118 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006119 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006120 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006121 goto start;
6122 }
6123 inquotes = !inquotes;
6124 addquote:
6125 if (quotes) {
6126 p--;
6127 length++;
6128 startloc++;
6129 }
6130 break;
6131 case CTLESC:
6132 startloc++;
6133 length++;
6134 goto addquote;
6135 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006136 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006137 goto start;
6138 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006139 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006140 case CTLBACKQ|CTLQUOTE:
6141 expbackq(argbackq->n, c, quotes);
6142 argbackq = argbackq->next;
6143 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006144#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006145 case CTLENDARI:
6146 p--;
6147 expari(quotes);
6148 goto start;
6149#endif
6150 }
6151 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006152 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006153}
6154
6155static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006156scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6157 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006158{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006159 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006160 char c;
6161
6162 loc = startp;
6163 loc2 = rmesc;
6164 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006165 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006167
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006168 c = *loc2;
6169 if (zero) {
6170 *loc2 = '\0';
6171 s = rmesc;
6172 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006173 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006174
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006175 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006176 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006177 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006178 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006179 loc++;
6180 loc++;
6181 loc2++;
6182 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006183 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006184}
6185
6186static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006187scanright(char *startp, char *rmesc, char *rmescend,
6188 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006189{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006190#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6191 int try2optimize = match_at_start;
6192#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006193 int esc = 0;
6194 char *loc;
6195 char *loc2;
6196
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006197 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6198 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6199 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6200 * Logic:
6201 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6202 * and on each iteration they go back two/one char until they reach the beginning.
6203 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6204 */
6205 /* TODO: document in what other circumstances we are called. */
6206
6207 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006208 int match;
6209 char c = *loc2;
6210 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006211 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006212 *loc2 = '\0';
6213 s = rmesc;
6214 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006215 match = pmatch(pattern, s);
6216 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006217 *loc2 = c;
6218 if (match)
6219 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006220#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6221 if (try2optimize) {
6222 /* Maybe we can optimize this:
6223 * if pattern ends with unescaped *, we can avoid checking
6224 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6225 * it wont match truncated "raw_value_of_" strings too.
6226 */
6227 unsigned plen = strlen(pattern);
6228 /* Does it end with "*"? */
6229 if (plen != 0 && pattern[--plen] == '*') {
6230 /* "xxxx*" is not escaped */
6231 /* "xxx\*" is escaped */
6232 /* "xx\\*" is not escaped */
6233 /* "x\\\*" is escaped */
6234 int slashes = 0;
6235 while (plen != 0 && pattern[--plen] == '\\')
6236 slashes++;
6237 if (!(slashes & 1))
6238 break; /* ends with unescaped "*" */
6239 }
6240 try2optimize = 0;
6241 }
6242#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006243 loc--;
6244 if (quotes) {
6245 if (--esc < 0) {
6246 esc = esclen(startp, loc);
6247 }
6248 if (esc % 2) {
6249 esc--;
6250 loc--;
6251 }
6252 }
6253 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006254 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006255}
6256
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006257static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006258static void
6259varunset(const char *end, const char *var, const char *umsg, int varflags)
6260{
6261 const char *msg;
6262 const char *tail;
6263
6264 tail = nullstr;
6265 msg = "parameter not set";
6266 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006267 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006268 if (varflags & VSNUL)
6269 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006270 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006271 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006272 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006274 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006275}
6276
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006277#if ENABLE_ASH_BASH_COMPAT
6278static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006279parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006280{
6281 char *idx, *repl = NULL;
6282 unsigned char c;
6283
Denys Vlasenko16149002010-08-06 22:06:21 +02006284 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006285 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006286 idx = arg;
6287 while (1) {
6288 c = *arg;
6289 if (!c)
6290 break;
6291 if (c == '/') {
6292 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006293 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006294 repl = idx + 1;
6295 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006296 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006297 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006298 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006299 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006300 /*
6301 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6302 * The result is a_\_z_c (not a\_\_z_c)!
6303 *
6304 * Enable debug prints in this function and you'll see:
6305 * ash: arg:'\\b/_\\_z_' varflags:d
6306 * ash: pattern:'\\b' repl:'_\_z_'
6307 * That is, \\b is interpreted as \\b, but \\_ as \_!
6308 * IOW: search pattern and replace string treat backslashes
6309 * differently! That is the reason why we check repl below:
6310 */
6311 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6312 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006313 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006314 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006315 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006316
6317 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006318}
6319#endif /* ENABLE_ASH_BASH_COMPAT */
6320
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006321static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006322subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006323 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006324{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006325 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326 char *startp;
6327 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006328 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006329 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006330 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006331 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006332 int saveherefd = herefd;
6333 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006334 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006335 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006337 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6338 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006339
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006340 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006341 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6342 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006343 STPUTC('\0', expdest);
6344 herefd = saveherefd;
6345 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006346 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006347
6348 switch (subtype) {
6349 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006350 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006351 amount = startp - expdest;
6352 STADJUST(amount, expdest);
6353 return startp;
6354
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006355 case VSQUESTION:
6356 varunset(p, varname, startp, varflags);
6357 /* NOTREACHED */
6358
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006359#if ENABLE_ASH_BASH_COMPAT
6360 case VSSUBSTR:
6361 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006362 /* Read POS in ${var:POS:LEN} */
6363 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006364 len = str - startp - 1;
6365
6366 /* *loc != '\0', guaranteed by parser */
6367 if (quotes) {
6368 char *ptr;
6369
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006370 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006371 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006372 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006373 len--;
6374 ptr++;
6375 }
6376 }
6377 }
6378 orig_len = len;
6379
6380 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006381 /* ${var::LEN} */
6382 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006383 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006384 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006385 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006386 while (*loc && *loc != ':') {
6387 /* TODO?
6388 * bash complains on: var=qwe; echo ${var:1a:123}
6389 if (!isdigit(*loc))
6390 ash_msg_and_raise_error(msg_illnum, str);
6391 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006392 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006393 }
6394 if (*loc++ == ':') {
6395 len = number(loc);
6396 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006397 }
6398 if (pos >= orig_len) {
6399 pos = 0;
6400 len = 0;
6401 }
6402 if (len > (orig_len - pos))
6403 len = orig_len - pos;
6404
6405 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006406 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006407 str++;
6408 }
6409 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006410 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006411 *loc++ = *str++;
6412 *loc++ = *str++;
6413 }
6414 *loc = '\0';
6415 amount = loc - expdest;
6416 STADJUST(amount, expdest);
6417 return loc;
6418#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006419 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006420
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006421 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006422
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006423 /* We'll comeback here if we grow the stack while handling
6424 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6425 * stack will need rebasing, and we'll need to remove our work
6426 * areas each time
6427 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006428 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006429
6430 amount = expdest - ((char *)stackblock() + resetloc);
6431 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006432 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006433
6434 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006435 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006436 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006437 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006438 if (rmesc != startp) {
6439 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006440 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 }
6442 }
6443 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006444 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006445 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006446 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006447
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006448#if ENABLE_ASH_BASH_COMPAT
6449 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006450 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006451
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006452 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006453 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006454 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006455 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006456 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006457 }
6458
6459 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006460 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006461 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006462
6463 len = 0;
6464 idx = startp;
6465 end = str - 1;
6466 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006467 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006468 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006469 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006470 if (!loc) {
6471 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006472 char *restart_detect = stackblock();
6473 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006474 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006475 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006476 idx++;
6477 len++;
6478 STPUTC(*idx, expdest);
6479 }
6480 if (stackblock() != restart_detect)
6481 goto restart;
6482 idx++;
6483 len++;
6484 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006485 /* continue; - prone to quadratic behavior, smarter code: */
6486 if (idx >= end)
6487 break;
6488 if (str[0] == '*') {
6489 /* Pattern is "*foo". If "*foo" does not match "long_string",
6490 * it would never match "ong_string" etc, no point in trying.
6491 */
6492 goto skip_matching;
6493 }
6494 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006495 }
6496
6497 if (subtype == VSREPLACEALL) {
6498 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006499 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006500 idx++;
6501 idx++;
6502 rmesc++;
6503 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006504 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006505 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006506 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006507
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006508 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006509 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006510 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006511 if (quotes && *loc == '\\') {
6512 STPUTC(CTLESC, expdest);
6513 len++;
6514 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006515 STPUTC(*loc, expdest);
6516 if (stackblock() != restart_detect)
6517 goto restart;
6518 len++;
6519 }
6520
6521 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006522 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006523 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006524 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006525 STPUTC(*idx, expdest);
6526 if (stackblock() != restart_detect)
6527 goto restart;
6528 len++;
6529 idx++;
6530 }
6531 break;
6532 }
6533 }
6534
6535 /* We've put the replaced text into a buffer at workloc, now
6536 * move it to the right place and adjust the stack.
6537 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006538 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006539 startp = (char *)stackblock() + startloc;
6540 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006541 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006542 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006543 STADJUST(-amount, expdest);
6544 return startp;
6545 }
6546#endif /* ENABLE_ASH_BASH_COMPAT */
6547
6548 subtype -= VSTRIMRIGHT;
6549#if DEBUG
6550 if (subtype < 0 || subtype > 7)
6551 abort();
6552#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006553 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006554 zero = subtype >> 1;
6555 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6556 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6557
6558 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6559 if (loc) {
6560 if (zero) {
6561 memmove(startp, loc, str - loc);
6562 loc = startp + (str - loc) - 1;
6563 }
6564 *loc = '\0';
6565 amount = loc - expdest;
6566 STADJUST(amount, expdest);
6567 }
6568 return loc;
6569}
6570
6571/*
6572 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006573 * name parameter (examples):
6574 * ash -c 'echo $1' name:'1='
6575 * ash -c 'echo $qwe' name:'qwe='
6576 * ash -c 'echo $$' name:'$='
6577 * ash -c 'echo ${$}' name:'$='
6578 * ash -c 'echo ${$##q}' name:'$=q'
6579 * ash -c 'echo ${#$}' name:'$='
6580 * note: examples with bad shell syntax:
6581 * ash -c 'echo ${#$1}' name:'$=1'
6582 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006583 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006584static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006585varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006586{
Mike Frysinger98c52642009-04-02 10:02:37 +00006587 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006588 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006589 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006590 int sepq = 0;
6591 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006592 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006593 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006594 int quoted = varflags & VSQUOTE;
6595 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006596
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006597 switch (*name) {
6598 case '$':
6599 num = rootpid;
6600 goto numvar;
6601 case '?':
6602 num = exitstatus;
6603 goto numvar;
6604 case '#':
6605 num = shellparam.nparam;
6606 goto numvar;
6607 case '!':
6608 num = backgndpid;
6609 if (num == 0)
6610 return -1;
6611 numvar:
6612 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006613 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006614 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006615 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006616 for (i = NOPTS - 1; i >= 0; i--) {
6617 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006618 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006619 len++;
6620 }
6621 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006622 check_1char_name:
6623#if 0
6624 /* handles cases similar to ${#$1} */
6625 if (name[2] != '\0')
6626 raise_error_syntax("bad substitution");
6627#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006628 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006629 case '@': {
6630 char **ap;
6631 int sep;
6632
6633 if (quoted && (flags & EXP_FULL)) {
6634 /* note: this is not meant as PEOF value */
6635 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006636 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006637 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006638 /* fall through */
6639 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006640 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006641 i = SIT(sep, syntax);
6642 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006643 sepq = 1;
6644 param:
6645 ap = shellparam.p;
6646 if (!ap)
6647 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006648 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006649 size_t partlen;
6650
6651 partlen = strlen(p);
6652 len += partlen;
6653
6654 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6655 memtodest(p, partlen, syntax, quotes);
6656
6657 if (*ap && sep) {
6658 char *q;
6659
6660 len++;
6661 if (subtype == VSPLUS || subtype == VSLENGTH) {
6662 continue;
6663 }
6664 q = expdest;
6665 if (sepq)
6666 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006667 /* note: may put NUL despite sep != 0
6668 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006669 STPUTC(sep, q);
6670 expdest = q;
6671 }
6672 }
6673 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006674 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006675 case '0':
6676 case '1':
6677 case '2':
6678 case '3':
6679 case '4':
6680 case '5':
6681 case '6':
6682 case '7':
6683 case '8':
6684 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006685 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006686 if (num < 0 || num > shellparam.nparam)
6687 return -1;
6688 p = num ? shellparam.p[num - 1] : arg0;
6689 goto value;
6690 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006691 /* NB: name has form "VAR=..." */
6692
6693 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6694 * which should be considered before we check variables. */
6695 if (var_str_list) {
6696 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6697 p = NULL;
6698 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006699 char *str, *eq;
6700 str = var_str_list->text;
6701 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006702 if (!eq) /* stop at first non-assignment */
6703 break;
6704 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006705 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006706 && strncmp(str, name, name_len) == 0
6707 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006708 p = eq;
6709 /* goto value; - WRONG! */
6710 /* think "A=1 A=2 B=$A" */
6711 }
6712 var_str_list = var_str_list->next;
6713 } while (var_str_list);
6714 if (p)
6715 goto value;
6716 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006717 p = lookupvar(name);
6718 value:
6719 if (!p)
6720 return -1;
6721
6722 len = strlen(p);
6723 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6724 memtodest(p, len, syntax, quotes);
6725 return len;
6726 }
6727
6728 if (subtype == VSPLUS || subtype == VSLENGTH)
6729 STADJUST(-len, expdest);
6730 return len;
6731}
6732
6733/*
6734 * Expand a variable, and return a pointer to the next character in the
6735 * input string.
6736 */
6737static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006738evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006739{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006740 char varflags;
6741 char subtype;
6742 char quoted;
6743 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006744 char *var;
6745 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006746 int startloc;
6747 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006748
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006749 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006750 subtype = varflags & VSTYPE;
6751 quoted = varflags & VSQUOTE;
6752 var = p;
6753 easy = (!quoted || (*var == '@' && shellparam.nparam));
6754 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006755 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006756
6757 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006758 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006759 if (varflags & VSNUL)
6760 varlen--;
6761
6762 if (subtype == VSPLUS) {
6763 varlen = -1 - varlen;
6764 goto vsplus;
6765 }
6766
6767 if (subtype == VSMINUS) {
6768 vsplus:
6769 if (varlen < 0) {
6770 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006771 p,
6772 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006773 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006774 );
6775 goto end;
6776 }
6777 if (easy)
6778 goto record;
6779 goto end;
6780 }
6781
6782 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6783 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006784 if (subevalvar(p, var, /* strloc: */ 0,
6785 subtype, startloc, varflags,
6786 /* quotes: */ 0,
6787 var_str_list)
6788 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006789 varflags &= ~VSNUL;
6790 /*
6791 * Remove any recorded regions beyond
6792 * start of variable
6793 */
6794 removerecordregions(startloc);
6795 goto again;
6796 }
6797 goto end;
6798 }
6799 if (easy)
6800 goto record;
6801 goto end;
6802 }
6803
6804 if (varlen < 0 && uflag)
6805 varunset(p, var, 0, 0);
6806
6807 if (subtype == VSLENGTH) {
6808 cvtnum(varlen > 0 ? varlen : 0);
6809 goto record;
6810 }
6811
6812 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006813 if (easy)
6814 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006815 goto end;
6816 }
6817
6818#if DEBUG
6819 switch (subtype) {
6820 case VSTRIMLEFT:
6821 case VSTRIMLEFTMAX:
6822 case VSTRIMRIGHT:
6823 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006824#if ENABLE_ASH_BASH_COMPAT
6825 case VSSUBSTR:
6826 case VSREPLACE:
6827 case VSREPLACEALL:
6828#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006829 break;
6830 default:
6831 abort();
6832 }
6833#endif
6834
6835 if (varlen >= 0) {
6836 /*
6837 * Terminate the string and start recording the pattern
6838 * right after it
6839 */
6840 STPUTC('\0', expdest);
6841 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006842 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006843 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006844//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006845 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006846 var_str_list)
6847 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006848 int amount = expdest - (
6849 (char *)stackblock() + patloc - 1
6850 );
6851 STADJUST(-amount, expdest);
6852 }
6853 /* Remove any recorded regions beyond start of variable */
6854 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006855 record:
6856 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006857 }
6858
6859 end:
6860 if (subtype != VSNORMAL) { /* skip to end of alternative */
6861 int nesting = 1;
6862 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006863 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006864 if (c == CTLESC)
6865 p++;
6866 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6867 if (varlen >= 0)
6868 argbackq = argbackq->next;
6869 } else if (c == CTLVAR) {
6870 if ((*p++ & VSTYPE) != VSNORMAL)
6871 nesting++;
6872 } else if (c == CTLENDVAR) {
6873 if (--nesting == 0)
6874 break;
6875 }
6876 }
6877 }
6878 return p;
6879}
6880
6881/*
6882 * Break the argument string into pieces based upon IFS and add the
6883 * strings to the argument list. The regions of the string to be
6884 * searched for IFS characters have been stored by recordregion.
6885 */
6886static void
6887ifsbreakup(char *string, struct arglist *arglist)
6888{
6889 struct ifsregion *ifsp;
6890 struct strlist *sp;
6891 char *start;
6892 char *p;
6893 char *q;
6894 const char *ifs, *realifs;
6895 int ifsspc;
6896 int nulonly;
6897
6898 start = string;
6899 if (ifslastp != NULL) {
6900 ifsspc = 0;
6901 nulonly = 0;
6902 realifs = ifsset() ? ifsval() : defifs;
6903 ifsp = &ifsfirst;
6904 do {
6905 p = string + ifsp->begoff;
6906 nulonly = ifsp->nulonly;
6907 ifs = nulonly ? nullstr : realifs;
6908 ifsspc = 0;
6909 while (p < string + ifsp->endoff) {
6910 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006911 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006912 p++;
6913 if (!strchr(ifs, *p)) {
6914 p++;
6915 continue;
6916 }
6917 if (!nulonly)
6918 ifsspc = (strchr(defifs, *p) != NULL);
6919 /* Ignore IFS whitespace at start */
6920 if (q == start && ifsspc) {
6921 p++;
6922 start = p;
6923 continue;
6924 }
6925 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006926 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006927 sp->text = start;
6928 *arglist->lastp = sp;
6929 arglist->lastp = &sp->next;
6930 p++;
6931 if (!nulonly) {
6932 for (;;) {
6933 if (p >= string + ifsp->endoff) {
6934 break;
6935 }
6936 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006937 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006939 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006940 p = q;
6941 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006942 }
6943 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006944 if (ifsspc) {
6945 p++;
6946 ifsspc = 0;
6947 } else {
6948 p = q;
6949 break;
6950 }
6951 } else
6952 p++;
6953 }
6954 }
6955 start = p;
6956 } /* while */
6957 ifsp = ifsp->next;
6958 } while (ifsp != NULL);
6959 if (nulonly)
6960 goto add;
6961 }
6962
6963 if (!*start)
6964 return;
6965
6966 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006967 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006968 sp->text = start;
6969 *arglist->lastp = sp;
6970 arglist->lastp = &sp->next;
6971}
6972
6973static void
6974ifsfree(void)
6975{
6976 struct ifsregion *p;
6977
6978 INT_OFF;
6979 p = ifsfirst.next;
6980 do {
6981 struct ifsregion *ifsp;
6982 ifsp = p->next;
6983 free(p);
6984 p = ifsp;
6985 } while (p);
6986 ifslastp = NULL;
6987 ifsfirst.next = NULL;
6988 INT_ON;
6989}
6990
6991/*
6992 * Add a file name to the list.
6993 */
6994static void
6995addfname(const char *name)
6996{
6997 struct strlist *sp;
6998
Denis Vlasenko597906c2008-02-20 16:38:54 +00006999 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007000 sp->text = ststrdup(name);
7001 *exparg.lastp = sp;
7002 exparg.lastp = &sp->next;
7003}
7004
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007005/*
7006 * Do metacharacter (i.e. *, ?, [...]) expansion.
7007 */
7008static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007009expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007010{
7011 char *p;
7012 const char *cp;
7013 char *start;
7014 char *endname;
7015 int metaflag;
7016 struct stat statb;
7017 DIR *dirp;
7018 struct dirent *dp;
7019 int atend;
7020 int matchdot;
7021
7022 metaflag = 0;
7023 start = name;
7024 for (p = name; *p; p++) {
7025 if (*p == '*' || *p == '?')
7026 metaflag = 1;
7027 else if (*p == '[') {
7028 char *q = p + 1;
7029 if (*q == '!')
7030 q++;
7031 for (;;) {
7032 if (*q == '\\')
7033 q++;
7034 if (*q == '/' || *q == '\0')
7035 break;
7036 if (*++q == ']') {
7037 metaflag = 1;
7038 break;
7039 }
7040 }
7041 } else if (*p == '\\')
7042 p++;
7043 else if (*p == '/') {
7044 if (metaflag)
7045 goto out;
7046 start = p + 1;
7047 }
7048 }
7049 out:
7050 if (metaflag == 0) { /* we've reached the end of the file name */
7051 if (enddir != expdir)
7052 metaflag++;
7053 p = name;
7054 do {
7055 if (*p == '\\')
7056 p++;
7057 *enddir++ = *p;
7058 } while (*p++);
7059 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7060 addfname(expdir);
7061 return;
7062 }
7063 endname = p;
7064 if (name < start) {
7065 p = name;
7066 do {
7067 if (*p == '\\')
7068 p++;
7069 *enddir++ = *p++;
7070 } while (p < start);
7071 }
7072 if (enddir == expdir) {
7073 cp = ".";
7074 } else if (enddir == expdir + 1 && *expdir == '/') {
7075 cp = "/";
7076 } else {
7077 cp = expdir;
7078 enddir[-1] = '\0';
7079 }
7080 dirp = opendir(cp);
7081 if (dirp == NULL)
7082 return;
7083 if (enddir != expdir)
7084 enddir[-1] = '/';
7085 if (*endname == 0) {
7086 atend = 1;
7087 } else {
7088 atend = 0;
7089 *endname++ = '\0';
7090 }
7091 matchdot = 0;
7092 p = start;
7093 if (*p == '\\')
7094 p++;
7095 if (*p == '.')
7096 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007097 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007098 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007099 continue;
7100 if (pmatch(start, dp->d_name)) {
7101 if (atend) {
7102 strcpy(enddir, dp->d_name);
7103 addfname(expdir);
7104 } else {
7105 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7106 continue;
7107 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007108 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007109 }
7110 }
7111 }
7112 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007113 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007114 endname[-1] = '/';
7115}
7116
7117static struct strlist *
7118msort(struct strlist *list, int len)
7119{
7120 struct strlist *p, *q = NULL;
7121 struct strlist **lpp;
7122 int half;
7123 int n;
7124
7125 if (len <= 1)
7126 return list;
7127 half = len >> 1;
7128 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007129 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007130 q = p;
7131 p = p->next;
7132 }
7133 q->next = NULL; /* terminate first half of list */
7134 q = msort(list, half); /* sort first half of list */
7135 p = msort(p, len - half); /* sort second half */
7136 lpp = &list;
7137 for (;;) {
7138#if ENABLE_LOCALE_SUPPORT
7139 if (strcoll(p->text, q->text) < 0)
7140#else
7141 if (strcmp(p->text, q->text) < 0)
7142#endif
7143 {
7144 *lpp = p;
7145 lpp = &p->next;
7146 p = *lpp;
7147 if (p == NULL) {
7148 *lpp = q;
7149 break;
7150 }
7151 } else {
7152 *lpp = q;
7153 lpp = &q->next;
7154 q = *lpp;
7155 if (q == NULL) {
7156 *lpp = p;
7157 break;
7158 }
7159 }
7160 }
7161 return list;
7162}
7163
7164/*
7165 * Sort the results of file name expansion. It calculates the number of
7166 * strings to sort and then calls msort (short for merge sort) to do the
7167 * work.
7168 */
7169static struct strlist *
7170expsort(struct strlist *str)
7171{
7172 int len;
7173 struct strlist *sp;
7174
7175 len = 0;
7176 for (sp = str; sp; sp = sp->next)
7177 len++;
7178 return msort(str, len);
7179}
7180
7181static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007182expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007183{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007184 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007185 '*', '?', '[', 0
7186 };
7187 /* TODO - EXP_REDIR */
7188
7189 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007190 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007191 struct strlist **savelastp;
7192 struct strlist *sp;
7193 char *p;
7194
7195 if (fflag)
7196 goto nometa;
7197 if (!strpbrk(str->text, metachars))
7198 goto nometa;
7199 savelastp = exparg.lastp;
7200
7201 INT_OFF;
7202 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7203 {
7204 int i = strlen(str->text);
7205 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7206 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007207 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007208 free(expdir);
7209 if (p != str->text)
7210 free(p);
7211 INT_ON;
7212 if (exparg.lastp == savelastp) {
7213 /*
7214 * no matches
7215 */
7216 nometa:
7217 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007218 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007219 exparg.lastp = &str->next;
7220 } else {
7221 *exparg.lastp = NULL;
7222 *savelastp = sp = expsort(*savelastp);
7223 while (sp->next != NULL)
7224 sp = sp->next;
7225 exparg.lastp = &sp->next;
7226 }
7227 str = str->next;
7228 }
7229}
7230
7231/*
7232 * Perform variable substitution and command substitution on an argument,
7233 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7234 * perform splitting and file name expansion. When arglist is NULL, perform
7235 * here document expansion.
7236 */
7237static void
7238expandarg(union node *arg, struct arglist *arglist, int flag)
7239{
7240 struct strlist *sp;
7241 char *p;
7242
7243 argbackq = arg->narg.backquote;
7244 STARTSTACKSTR(expdest);
7245 ifsfirst.next = NULL;
7246 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007247 argstr(arg->narg.text, flag,
7248 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007249 p = _STPUTC('\0', expdest);
7250 expdest = p - 1;
7251 if (arglist == NULL) {
7252 return; /* here document expanded */
7253 }
7254 p = grabstackstr(p);
7255 exparg.lastp = &exparg.list;
7256 /*
7257 * TODO - EXP_REDIR
7258 */
7259 if (flag & EXP_FULL) {
7260 ifsbreakup(p, &exparg);
7261 *exparg.lastp = NULL;
7262 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007263 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007264 } else {
7265 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007266 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007267 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007268 sp->text = p;
7269 *exparg.lastp = sp;
7270 exparg.lastp = &sp->next;
7271 }
7272 if (ifsfirst.next)
7273 ifsfree();
7274 *exparg.lastp = NULL;
7275 if (exparg.list) {
7276 *arglist->lastp = exparg.list;
7277 arglist->lastp = exparg.lastp;
7278 }
7279}
7280
7281/*
7282 * Expand shell variables and backquotes inside a here document.
7283 */
7284static void
7285expandhere(union node *arg, int fd)
7286{
7287 herefd = fd;
7288 expandarg(arg, (struct arglist *)NULL, 0);
7289 full_write(fd, stackblock(), expdest - (char *)stackblock());
7290}
7291
7292/*
7293 * Returns true if the pattern matches the string.
7294 */
7295static int
7296patmatch(char *pattern, const char *string)
7297{
7298 return pmatch(preglob(pattern, 0, 0), string);
7299}
7300
7301/*
7302 * See if a pattern matches in a case statement.
7303 */
7304static int
7305casematch(union node *pattern, char *val)
7306{
7307 struct stackmark smark;
7308 int result;
7309
7310 setstackmark(&smark);
7311 argbackq = pattern->narg.backquote;
7312 STARTSTACKSTR(expdest);
7313 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007314 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7315 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007316 STACKSTRNUL(expdest);
7317 result = patmatch(stackblock(), val);
7318 popstackmark(&smark);
7319 return result;
7320}
7321
7322
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007323/* ============ find_command */
7324
7325struct builtincmd {
7326 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007327 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007328 /* unsigned flags; */
7329};
7330#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007331/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007332 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007334#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007335
7336struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007337 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007338 union param {
7339 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007340 /* index >= 0 for commands without path (slashes) */
7341 /* (TODO: what exactly does the value mean? PATH position?) */
7342 /* index == -1 for commands with slashes */
7343 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007344 const struct builtincmd *cmd;
7345 struct funcnode *func;
7346 } u;
7347};
7348/* values of cmdtype */
7349#define CMDUNKNOWN -1 /* no entry in table for command */
7350#define CMDNORMAL 0 /* command is an executable program */
7351#define CMDFUNCTION 1 /* command is a shell function */
7352#define CMDBUILTIN 2 /* command is a shell builtin */
7353
7354/* action to find_command() */
7355#define DO_ERR 0x01 /* prints errors */
7356#define DO_ABS 0x02 /* checks absolute paths */
7357#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7358#define DO_ALTPATH 0x08 /* using alternate path */
7359#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7360
7361static void find_command(char *, struct cmdentry *, int, const char *);
7362
7363
7364/* ============ Hashing commands */
7365
7366/*
7367 * When commands are first encountered, they are entered in a hash table.
7368 * This ensures that a full path search will not have to be done for them
7369 * on each invocation.
7370 *
7371 * We should investigate converting to a linear search, even though that
7372 * would make the command name "hash" a misnomer.
7373 */
7374
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007375struct tblentry {
7376 struct tblentry *next; /* next entry in hash chain */
7377 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007378 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007379 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007380 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007381};
7382
Denis Vlasenko01631112007-12-16 17:20:38 +00007383static struct tblentry **cmdtable;
7384#define INIT_G_cmdtable() do { \
7385 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7386} while (0)
7387
7388static int builtinloc = -1; /* index in path of %builtin, or -1 */
7389
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390
7391static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007392tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007393{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007394#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007395 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007396 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007397 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007398 while (*envp)
7399 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007400 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007401 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007402 /* re-exec ourselves with the new arguments */
7403 execve(bb_busybox_exec_path, argv, envp);
7404 /* If they called chroot or otherwise made the binary no longer
7405 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007406 }
7407#endif
7408
7409 repeat:
7410#ifdef SYSV
7411 do {
7412 execve(cmd, argv, envp);
7413 } while (errno == EINTR);
7414#else
7415 execve(cmd, argv, envp);
7416#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007417 if (cmd == (char*) bb_busybox_exec_path) {
7418 /* We already visited ENOEXEC branch below, don't do it again */
7419//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007420 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007421 return;
7422 }
7423 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007424 /* Run "cmd" as a shell script:
7425 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7426 * "If the execve() function fails with ENOEXEC, the shell
7427 * shall execute a command equivalent to having a shell invoked
7428 * with the command name as its first operand,
7429 * with any remaining arguments passed to the new shell"
7430 *
7431 * That is, do not use $SHELL, user's shell, or /bin/sh;
7432 * just call ourselves.
7433 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007434 char **ap;
7435 char **new;
7436
7437 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007438 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007439 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7440 new[0] = (char*) "ash";
7441 new[1] = cmd;
7442 ap = new + 2;
7443 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007444 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007445 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007446 argv = new;
7447 goto repeat;
7448 }
7449}
7450
7451/*
7452 * Exec a program. Never returns. If you change this routine, you may
7453 * have to change the find_command routine as well.
7454 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007455static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007456static void
7457shellexec(char **argv, const char *path, int idx)
7458{
7459 char *cmdname;
7460 int e;
7461 char **envp;
7462 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007463#if ENABLE_FEATURE_SH_STANDALONE
7464 int applet_no = -1;
7465#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007466
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007467 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007468 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007469 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007470#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007471 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007472#endif
7473 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007474 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007475 e = errno;
7476 } else {
7477 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007478 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007479 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007480 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007481 if (errno != ENOENT && errno != ENOTDIR)
7482 e = errno;
7483 }
7484 stunalloc(cmdname);
7485 }
7486 }
7487
7488 /* Map to POSIX errors */
7489 switch (e) {
7490 case EACCES:
7491 exerrno = 126;
7492 break;
7493 case ENOENT:
7494 exerrno = 127;
7495 break;
7496 default:
7497 exerrno = 2;
7498 break;
7499 }
7500 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007501 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7502 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007503 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7504 /* NOTREACHED */
7505}
7506
7507static void
7508printentry(struct tblentry *cmdp)
7509{
7510 int idx;
7511 const char *path;
7512 char *name;
7513
7514 idx = cmdp->param.index;
7515 path = pathval();
7516 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007517 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007518 stunalloc(name);
7519 } while (--idx >= 0);
7520 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7521}
7522
7523/*
7524 * Clear out command entries. The argument specifies the first entry in
7525 * PATH which has changed.
7526 */
7527static void
7528clearcmdentry(int firstchange)
7529{
7530 struct tblentry **tblp;
7531 struct tblentry **pp;
7532 struct tblentry *cmdp;
7533
7534 INT_OFF;
7535 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7536 pp = tblp;
7537 while ((cmdp = *pp) != NULL) {
7538 if ((cmdp->cmdtype == CMDNORMAL &&
7539 cmdp->param.index >= firstchange)
7540 || (cmdp->cmdtype == CMDBUILTIN &&
7541 builtinloc >= firstchange)
7542 ) {
7543 *pp = cmdp->next;
7544 free(cmdp);
7545 } else {
7546 pp = &cmdp->next;
7547 }
7548 }
7549 }
7550 INT_ON;
7551}
7552
7553/*
7554 * Locate a command in the command hash table. If "add" is nonzero,
7555 * add the command to the table if it is not already present. The
7556 * variable "lastcmdentry" is set to point to the address of the link
7557 * pointing to the entry, so that delete_cmd_entry can delete the
7558 * entry.
7559 *
7560 * Interrupts must be off if called with add != 0.
7561 */
7562static struct tblentry **lastcmdentry;
7563
7564static struct tblentry *
7565cmdlookup(const char *name, int add)
7566{
7567 unsigned int hashval;
7568 const char *p;
7569 struct tblentry *cmdp;
7570 struct tblentry **pp;
7571
7572 p = name;
7573 hashval = (unsigned char)*p << 4;
7574 while (*p)
7575 hashval += (unsigned char)*p++;
7576 hashval &= 0x7FFF;
7577 pp = &cmdtable[hashval % CMDTABLESIZE];
7578 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7579 if (strcmp(cmdp->cmdname, name) == 0)
7580 break;
7581 pp = &cmdp->next;
7582 }
7583 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007584 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7585 + strlen(name)
7586 /* + 1 - already done because
7587 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007588 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007589 cmdp->cmdtype = CMDUNKNOWN;
7590 strcpy(cmdp->cmdname, name);
7591 }
7592 lastcmdentry = pp;
7593 return cmdp;
7594}
7595
7596/*
7597 * Delete the command entry returned on the last lookup.
7598 */
7599static void
7600delete_cmd_entry(void)
7601{
7602 struct tblentry *cmdp;
7603
7604 INT_OFF;
7605 cmdp = *lastcmdentry;
7606 *lastcmdentry = cmdp->next;
7607 if (cmdp->cmdtype == CMDFUNCTION)
7608 freefunc(cmdp->param.func);
7609 free(cmdp);
7610 INT_ON;
7611}
7612
7613/*
7614 * Add a new command entry, replacing any existing command entry for
7615 * the same name - except special builtins.
7616 */
7617static void
7618addcmdentry(char *name, struct cmdentry *entry)
7619{
7620 struct tblentry *cmdp;
7621
7622 cmdp = cmdlookup(name, 1);
7623 if (cmdp->cmdtype == CMDFUNCTION) {
7624 freefunc(cmdp->param.func);
7625 }
7626 cmdp->cmdtype = entry->cmdtype;
7627 cmdp->param = entry->u;
7628 cmdp->rehash = 0;
7629}
7630
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007631static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007632hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007633{
7634 struct tblentry **pp;
7635 struct tblentry *cmdp;
7636 int c;
7637 struct cmdentry entry;
7638 char *name;
7639
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007640 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007641 clearcmdentry(0);
7642 return 0;
7643 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007644
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007645 if (*argptr == NULL) {
7646 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7647 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7648 if (cmdp->cmdtype == CMDNORMAL)
7649 printentry(cmdp);
7650 }
7651 }
7652 return 0;
7653 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007654
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007655 c = 0;
7656 while ((name = *argptr) != NULL) {
7657 cmdp = cmdlookup(name, 0);
7658 if (cmdp != NULL
7659 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007660 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7661 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007663 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007664 find_command(name, &entry, DO_ERR, pathval());
7665 if (entry.cmdtype == CMDUNKNOWN)
7666 c = 1;
7667 argptr++;
7668 }
7669 return c;
7670}
7671
7672/*
7673 * Called when a cd is done. Marks all commands so the next time they
7674 * are executed they will be rehashed.
7675 */
7676static void
7677hashcd(void)
7678{
7679 struct tblentry **pp;
7680 struct tblentry *cmdp;
7681
7682 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7683 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007684 if (cmdp->cmdtype == CMDNORMAL
7685 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007686 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007687 && builtinloc > 0)
7688 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007689 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007690 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007691 }
7692 }
7693}
7694
7695/*
7696 * Fix command hash table when PATH changed.
7697 * Called before PATH is changed. The argument is the new value of PATH;
7698 * pathval() still returns the old value at this point.
7699 * Called with interrupts off.
7700 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007701static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007702changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007703{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007704 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007705 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007706 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007707 int idx_bltin;
7708
7709 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007710 firstchange = 9999; /* assume no change */
7711 idx = 0;
7712 idx_bltin = -1;
7713 for (;;) {
7714 if (*old != *new) {
7715 firstchange = idx;
7716 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007717 || (*old == ':' && *new == '\0')
7718 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007719 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007720 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007721 old = new; /* ignore subsequent differences */
7722 }
7723 if (*new == '\0')
7724 break;
7725 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7726 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007727 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007728 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007729 new++;
7730 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007731 }
7732 if (builtinloc < 0 && idx_bltin >= 0)
7733 builtinloc = idx_bltin; /* zap builtins */
7734 if (builtinloc >= 0 && idx_bltin < 0)
7735 firstchange = 0;
7736 clearcmdentry(firstchange);
7737 builtinloc = idx_bltin;
7738}
7739
7740#define TEOF 0
7741#define TNL 1
7742#define TREDIR 2
7743#define TWORD 3
7744#define TSEMI 4
7745#define TBACKGND 5
7746#define TAND 6
7747#define TOR 7
7748#define TPIPE 8
7749#define TLP 9
7750#define TRP 10
7751#define TENDCASE 11
7752#define TENDBQUOTE 12
7753#define TNOT 13
7754#define TCASE 14
7755#define TDO 15
7756#define TDONE 16
7757#define TELIF 17
7758#define TELSE 18
7759#define TESAC 19
7760#define TFI 20
7761#define TFOR 21
7762#define TIF 22
7763#define TIN 23
7764#define TTHEN 24
7765#define TUNTIL 25
7766#define TWHILE 26
7767#define TBEGIN 27
7768#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007769typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007770
7771/* first char is indicating which tokens mark the end of a list */
7772static const char *const tokname_array[] = {
7773 "\1end of file",
7774 "\0newline",
7775 "\0redirection",
7776 "\0word",
7777 "\0;",
7778 "\0&",
7779 "\0&&",
7780 "\0||",
7781 "\0|",
7782 "\0(",
7783 "\1)",
7784 "\1;;",
7785 "\1`",
7786#define KWDOFFSET 13
7787 /* the following are keywords */
7788 "\0!",
7789 "\0case",
7790 "\1do",
7791 "\1done",
7792 "\1elif",
7793 "\1else",
7794 "\1esac",
7795 "\1fi",
7796 "\0for",
7797 "\0if",
7798 "\0in",
7799 "\1then",
7800 "\0until",
7801 "\0while",
7802 "\0{",
7803 "\1}",
7804};
7805
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007806/* Wrapper around strcmp for qsort/bsearch/... */
7807static int
7808pstrcmp(const void *a, const void *b)
7809{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007810 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007811}
7812
7813static const char *const *
7814findkwd(const char *s)
7815{
7816 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007817 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7818 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007819}
7820
7821/*
7822 * Locate and print what a word is...
7823 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007824static int
7825describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007826{
7827 struct cmdentry entry;
7828 struct tblentry *cmdp;
7829#if ENABLE_ASH_ALIAS
7830 const struct alias *ap;
7831#endif
7832 const char *path = pathval();
7833
7834 if (describe_command_verbose) {
7835 out1str(command);
7836 }
7837
7838 /* First look at the keywords */
7839 if (findkwd(command)) {
7840 out1str(describe_command_verbose ? " is a shell keyword" : command);
7841 goto out;
7842 }
7843
7844#if ENABLE_ASH_ALIAS
7845 /* Then look at the aliases */
7846 ap = lookupalias(command, 0);
7847 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007848 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007849 out1str("alias ");
7850 printalias(ap);
7851 return 0;
7852 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007853 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007854 goto out;
7855 }
7856#endif
7857 /* Then check if it is a tracked alias */
7858 cmdp = cmdlookup(command, 0);
7859 if (cmdp != NULL) {
7860 entry.cmdtype = cmdp->cmdtype;
7861 entry.u = cmdp->param;
7862 } else {
7863 /* Finally use brute force */
7864 find_command(command, &entry, DO_ABS, path);
7865 }
7866
7867 switch (entry.cmdtype) {
7868 case CMDNORMAL: {
7869 int j = entry.u.index;
7870 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007871 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007872 p = command;
7873 } else {
7874 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007875 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007876 stunalloc(p);
7877 } while (--j >= 0);
7878 }
7879 if (describe_command_verbose) {
7880 out1fmt(" is%s %s",
7881 (cmdp ? " a tracked alias for" : nullstr), p
7882 );
7883 } else {
7884 out1str(p);
7885 }
7886 break;
7887 }
7888
7889 case CMDFUNCTION:
7890 if (describe_command_verbose) {
7891 out1str(" is a shell function");
7892 } else {
7893 out1str(command);
7894 }
7895 break;
7896
7897 case CMDBUILTIN:
7898 if (describe_command_verbose) {
7899 out1fmt(" is a %sshell builtin",
7900 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7901 "special " : nullstr
7902 );
7903 } else {
7904 out1str(command);
7905 }
7906 break;
7907
7908 default:
7909 if (describe_command_verbose) {
7910 out1str(": not found\n");
7911 }
7912 return 127;
7913 }
7914 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007915 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007916 return 0;
7917}
7918
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007919static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007920typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007921{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007922 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007923 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007924 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007925
Denis Vlasenko46846e22007-05-20 13:08:31 +00007926 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007927 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007928 i++;
7929 verbose = 0;
7930 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007931 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007932 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007933 }
7934 return err;
7935}
7936
7937#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007938static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007939commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007940{
7941 int c;
7942 enum {
7943 VERIFY_BRIEF = 1,
7944 VERIFY_VERBOSE = 2,
7945 } verify = 0;
7946
7947 while ((c = nextopt("pvV")) != '\0')
7948 if (c == 'V')
7949 verify |= VERIFY_VERBOSE;
7950 else if (c == 'v')
7951 verify |= VERIFY_BRIEF;
7952#if DEBUG
7953 else if (c != 'p')
7954 abort();
7955#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007956 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7957 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007958 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007959 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007960
7961 return 0;
7962}
7963#endif
7964
7965
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007966/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007967
Denis Vlasenko340299a2008-11-21 10:36:36 +00007968static int funcblocksize; /* size of structures in function */
7969static int funcstringsize; /* size of strings in node */
7970static void *funcblock; /* block to allocate function from */
7971static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007972
Eric Andersencb57d552001-06-28 07:25:16 +00007973/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007974#define EV_EXIT 01 /* exit after evaluating tree */
7975#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007976#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007977
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007978static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007979 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7980 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7981 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7982 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7983 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7984 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7985 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7986 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7987 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7988 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7989 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7990 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7991 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7992 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7993 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7994 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7995 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007996#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007997 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007998#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007999 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8000 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8001 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8002 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8003 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8004 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8005 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8006 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8007 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008008};
8009
8010static void calcsize(union node *n);
8011
8012static void
8013sizenodelist(struct nodelist *lp)
8014{
8015 while (lp) {
8016 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8017 calcsize(lp->n);
8018 lp = lp->next;
8019 }
8020}
8021
8022static void
8023calcsize(union node *n)
8024{
8025 if (n == NULL)
8026 return;
8027 funcblocksize += nodesize[n->type];
8028 switch (n->type) {
8029 case NCMD:
8030 calcsize(n->ncmd.redirect);
8031 calcsize(n->ncmd.args);
8032 calcsize(n->ncmd.assign);
8033 break;
8034 case NPIPE:
8035 sizenodelist(n->npipe.cmdlist);
8036 break;
8037 case NREDIR:
8038 case NBACKGND:
8039 case NSUBSHELL:
8040 calcsize(n->nredir.redirect);
8041 calcsize(n->nredir.n);
8042 break;
8043 case NAND:
8044 case NOR:
8045 case NSEMI:
8046 case NWHILE:
8047 case NUNTIL:
8048 calcsize(n->nbinary.ch2);
8049 calcsize(n->nbinary.ch1);
8050 break;
8051 case NIF:
8052 calcsize(n->nif.elsepart);
8053 calcsize(n->nif.ifpart);
8054 calcsize(n->nif.test);
8055 break;
8056 case NFOR:
8057 funcstringsize += strlen(n->nfor.var) + 1;
8058 calcsize(n->nfor.body);
8059 calcsize(n->nfor.args);
8060 break;
8061 case NCASE:
8062 calcsize(n->ncase.cases);
8063 calcsize(n->ncase.expr);
8064 break;
8065 case NCLIST:
8066 calcsize(n->nclist.body);
8067 calcsize(n->nclist.pattern);
8068 calcsize(n->nclist.next);
8069 break;
8070 case NDEFUN:
8071 case NARG:
8072 sizenodelist(n->narg.backquote);
8073 funcstringsize += strlen(n->narg.text) + 1;
8074 calcsize(n->narg.next);
8075 break;
8076 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008077#if ENABLE_ASH_BASH_COMPAT
8078 case NTO2:
8079#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008080 case NCLOBBER:
8081 case NFROM:
8082 case NFROMTO:
8083 case NAPPEND:
8084 calcsize(n->nfile.fname);
8085 calcsize(n->nfile.next);
8086 break;
8087 case NTOFD:
8088 case NFROMFD:
8089 calcsize(n->ndup.vname);
8090 calcsize(n->ndup.next);
8091 break;
8092 case NHERE:
8093 case NXHERE:
8094 calcsize(n->nhere.doc);
8095 calcsize(n->nhere.next);
8096 break;
8097 case NNOT:
8098 calcsize(n->nnot.com);
8099 break;
8100 };
8101}
8102
8103static char *
8104nodeckstrdup(char *s)
8105{
8106 char *rtn = funcstring;
8107
8108 strcpy(funcstring, s);
8109 funcstring += strlen(s) + 1;
8110 return rtn;
8111}
8112
8113static union node *copynode(union node *);
8114
8115static struct nodelist *
8116copynodelist(struct nodelist *lp)
8117{
8118 struct nodelist *start;
8119 struct nodelist **lpp;
8120
8121 lpp = &start;
8122 while (lp) {
8123 *lpp = funcblock;
8124 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8125 (*lpp)->n = copynode(lp->n);
8126 lp = lp->next;
8127 lpp = &(*lpp)->next;
8128 }
8129 *lpp = NULL;
8130 return start;
8131}
8132
8133static union node *
8134copynode(union node *n)
8135{
8136 union node *new;
8137
8138 if (n == NULL)
8139 return NULL;
8140 new = funcblock;
8141 funcblock = (char *) funcblock + nodesize[n->type];
8142
8143 switch (n->type) {
8144 case NCMD:
8145 new->ncmd.redirect = copynode(n->ncmd.redirect);
8146 new->ncmd.args = copynode(n->ncmd.args);
8147 new->ncmd.assign = copynode(n->ncmd.assign);
8148 break;
8149 case NPIPE:
8150 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008151 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008152 break;
8153 case NREDIR:
8154 case NBACKGND:
8155 case NSUBSHELL:
8156 new->nredir.redirect = copynode(n->nredir.redirect);
8157 new->nredir.n = copynode(n->nredir.n);
8158 break;
8159 case NAND:
8160 case NOR:
8161 case NSEMI:
8162 case NWHILE:
8163 case NUNTIL:
8164 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8165 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8166 break;
8167 case NIF:
8168 new->nif.elsepart = copynode(n->nif.elsepart);
8169 new->nif.ifpart = copynode(n->nif.ifpart);
8170 new->nif.test = copynode(n->nif.test);
8171 break;
8172 case NFOR:
8173 new->nfor.var = nodeckstrdup(n->nfor.var);
8174 new->nfor.body = copynode(n->nfor.body);
8175 new->nfor.args = copynode(n->nfor.args);
8176 break;
8177 case NCASE:
8178 new->ncase.cases = copynode(n->ncase.cases);
8179 new->ncase.expr = copynode(n->ncase.expr);
8180 break;
8181 case NCLIST:
8182 new->nclist.body = copynode(n->nclist.body);
8183 new->nclist.pattern = copynode(n->nclist.pattern);
8184 new->nclist.next = copynode(n->nclist.next);
8185 break;
8186 case NDEFUN:
8187 case NARG:
8188 new->narg.backquote = copynodelist(n->narg.backquote);
8189 new->narg.text = nodeckstrdup(n->narg.text);
8190 new->narg.next = copynode(n->narg.next);
8191 break;
8192 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008193#if ENABLE_ASH_BASH_COMPAT
8194 case NTO2:
8195#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008196 case NCLOBBER:
8197 case NFROM:
8198 case NFROMTO:
8199 case NAPPEND:
8200 new->nfile.fname = copynode(n->nfile.fname);
8201 new->nfile.fd = n->nfile.fd;
8202 new->nfile.next = copynode(n->nfile.next);
8203 break;
8204 case NTOFD:
8205 case NFROMFD:
8206 new->ndup.vname = copynode(n->ndup.vname);
8207 new->ndup.dupfd = n->ndup.dupfd;
8208 new->ndup.fd = n->ndup.fd;
8209 new->ndup.next = copynode(n->ndup.next);
8210 break;
8211 case NHERE:
8212 case NXHERE:
8213 new->nhere.doc = copynode(n->nhere.doc);
8214 new->nhere.fd = n->nhere.fd;
8215 new->nhere.next = copynode(n->nhere.next);
8216 break;
8217 case NNOT:
8218 new->nnot.com = copynode(n->nnot.com);
8219 break;
8220 };
8221 new->type = n->type;
8222 return new;
8223}
8224
8225/*
8226 * Make a copy of a parse tree.
8227 */
8228static struct funcnode *
8229copyfunc(union node *n)
8230{
8231 struct funcnode *f;
8232 size_t blocksize;
8233
8234 funcblocksize = offsetof(struct funcnode, n);
8235 funcstringsize = 0;
8236 calcsize(n);
8237 blocksize = funcblocksize;
8238 f = ckmalloc(blocksize + funcstringsize);
8239 funcblock = (char *) f + offsetof(struct funcnode, n);
8240 funcstring = (char *) f + blocksize;
8241 copynode(n);
8242 f->count = 0;
8243 return f;
8244}
8245
8246/*
8247 * Define a shell function.
8248 */
8249static void
8250defun(char *name, union node *func)
8251{
8252 struct cmdentry entry;
8253
8254 INT_OFF;
8255 entry.cmdtype = CMDFUNCTION;
8256 entry.u.func = copyfunc(func);
8257 addcmdentry(name, &entry);
8258 INT_ON;
8259}
8260
Denis Vlasenko4b875702009-03-19 13:30:04 +00008261/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008262#define SKIPBREAK (1 << 0)
8263#define SKIPCONT (1 << 1)
8264#define SKIPFUNC (1 << 2)
8265#define SKIPFILE (1 << 3)
8266#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008267static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008268static int skipcount; /* number of levels to skip */
8269static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008270static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008271
Denis Vlasenko4b875702009-03-19 13:30:04 +00008272/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008273static int evalstring(char *s, int mask);
8274
Denis Vlasenko4b875702009-03-19 13:30:04 +00008275/* Called to execute a trap.
8276 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008277 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008278 *
8279 * Perhaps we should avoid entering new trap handlers
8280 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008281 */
8282static int
8283dotrap(void)
8284{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008285 uint8_t *g;
8286 int sig;
8287 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008288
8289 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008290 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008291 xbarrier();
8292
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008293 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008294 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8295 int want_exexit;
8296 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008297
Denis Vlasenko4b875702009-03-19 13:30:04 +00008298 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008299 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008300 t = trap[sig];
8301 /* non-trapped SIGINT is handled separately by raise_interrupt,
8302 * don't upset it by resetting gotsig[SIGINT-1] */
8303 if (sig == SIGINT && !t)
8304 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008305
8306 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008307 *g = 0;
8308 if (!t)
8309 continue;
8310 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008311 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008312 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008313 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008314 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008315 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008316 }
8317
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008318 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008319 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008320}
8321
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008322/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008323static void evalloop(union node *, int);
8324static void evalfor(union node *, int);
8325static void evalcase(union node *, int);
8326static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008327static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008328static void evalpipe(union node *, int);
8329static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008330static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008331static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008332
Eric Andersen62483552001-07-10 06:09:16 +00008333/*
Eric Andersenc470f442003-07-28 09:56:35 +00008334 * Evaluate a parse tree. The value is left in the global variable
8335 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008336 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008337static void
Eric Andersenc470f442003-07-28 09:56:35 +00008338evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008339{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008340 struct jmploc *volatile savehandler = exception_handler;
8341 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008342 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008343 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008344 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008345 int int_level;
8346
8347 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008348
Eric Andersenc470f442003-07-28 09:56:35 +00008349 if (n == NULL) {
8350 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008351 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008352 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008353 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008354
8355 exception_handler = &jmploc;
8356 {
8357 int err = setjmp(jmploc.loc);
8358 if (err) {
8359 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008360 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008361 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8362 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008363 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008364 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008365 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008366 TRACE(("exception %d in evaltree, propagating err=%d\n",
8367 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008368 exception_handler = savehandler;
8369 longjmp(exception_handler->loc, err);
8370 }
8371 }
8372
Eric Andersenc470f442003-07-28 09:56:35 +00008373 switch (n->type) {
8374 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008375#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008376 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008377 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008378 break;
8379#endif
8380 case NNOT:
8381 evaltree(n->nnot.com, EV_TESTED);
8382 status = !exitstatus;
8383 goto setstatus;
8384 case NREDIR:
8385 expredir(n->nredir.redirect);
8386 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8387 if (!status) {
8388 evaltree(n->nredir.n, flags & EV_TESTED);
8389 status = exitstatus;
8390 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008391 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008392 goto setstatus;
8393 case NCMD:
8394 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008395 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008396 if (eflag && !(flags & EV_TESTED))
8397 checkexit = ~0;
8398 goto calleval;
8399 case NFOR:
8400 evalfn = evalfor;
8401 goto calleval;
8402 case NWHILE:
8403 case NUNTIL:
8404 evalfn = evalloop;
8405 goto calleval;
8406 case NSUBSHELL:
8407 case NBACKGND:
8408 evalfn = evalsubshell;
8409 goto calleval;
8410 case NPIPE:
8411 evalfn = evalpipe;
8412 goto checkexit;
8413 case NCASE:
8414 evalfn = evalcase;
8415 goto calleval;
8416 case NAND:
8417 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008418 case NSEMI: {
8419
Eric Andersenc470f442003-07-28 09:56:35 +00008420#if NAND + 1 != NOR
8421#error NAND + 1 != NOR
8422#endif
8423#if NOR + 1 != NSEMI
8424#error NOR + 1 != NSEMI
8425#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008426 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008427 evaltree(
8428 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008429 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008430 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008431 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008432 break;
8433 if (!evalskip) {
8434 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008435 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008436 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008437 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008438 evalfn(n, flags);
8439 break;
8440 }
8441 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008442 }
Eric Andersenc470f442003-07-28 09:56:35 +00008443 case NIF:
8444 evaltree(n->nif.test, EV_TESTED);
8445 if (evalskip)
8446 break;
8447 if (exitstatus == 0) {
8448 n = n->nif.ifpart;
8449 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008450 }
8451 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008452 n = n->nif.elsepart;
8453 goto evaln;
8454 }
8455 goto success;
8456 case NDEFUN:
8457 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008458 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008459 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008460 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008461 exitstatus = status;
8462 break;
8463 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008464
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008465 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008466 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008467
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008468 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008469 /* Order of checks below is important:
8470 * signal handlers trigger before exit caused by "set -e".
8471 */
8472 if (pending_sig && dotrap())
8473 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008474 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008475 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008476
8477 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008478 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008479 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008480 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008481
8482 RESTORE_INT(int_level);
8483 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008484}
8485
Eric Andersenc470f442003-07-28 09:56:35 +00008486#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8487static
8488#endif
8489void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8490
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008491static void
Eric Andersenc470f442003-07-28 09:56:35 +00008492evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008493{
8494 int status;
8495
8496 loopnest++;
8497 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008498 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008499 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008500 int i;
8501
Eric Andersencb57d552001-06-28 07:25:16 +00008502 evaltree(n->nbinary.ch1, EV_TESTED);
8503 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008504 skipping:
8505 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008506 evalskip = 0;
8507 continue;
8508 }
8509 if (evalskip == SKIPBREAK && --skipcount <= 0)
8510 evalskip = 0;
8511 break;
8512 }
Eric Andersenc470f442003-07-28 09:56:35 +00008513 i = exitstatus;
8514 if (n->type != NWHILE)
8515 i = !i;
8516 if (i != 0)
8517 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008518 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008519 status = exitstatus;
8520 if (evalskip)
8521 goto skipping;
8522 }
8523 loopnest--;
8524 exitstatus = status;
8525}
8526
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008527static void
Eric Andersenc470f442003-07-28 09:56:35 +00008528evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008529{
8530 struct arglist arglist;
8531 union node *argp;
8532 struct strlist *sp;
8533 struct stackmark smark;
8534
8535 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008536 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008537 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008538 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008539 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008540 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008541 if (evalskip)
8542 goto out;
8543 }
8544 *arglist.lastp = NULL;
8545
8546 exitstatus = 0;
8547 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008548 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008549 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008550 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008551 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008552 if (evalskip) {
8553 if (evalskip == SKIPCONT && --skipcount <= 0) {
8554 evalskip = 0;
8555 continue;
8556 }
8557 if (evalskip == SKIPBREAK && --skipcount <= 0)
8558 evalskip = 0;
8559 break;
8560 }
8561 }
8562 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008563 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008564 popstackmark(&smark);
8565}
8566
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008567static void
Eric Andersenc470f442003-07-28 09:56:35 +00008568evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008569{
8570 union node *cp;
8571 union node *patp;
8572 struct arglist arglist;
8573 struct stackmark smark;
8574
8575 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008576 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008577 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008578 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008579 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008580 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8581 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008582 if (casematch(patp, arglist.list->text)) {
8583 if (evalskip == 0) {
8584 evaltree(cp->nclist.body, flags);
8585 }
8586 goto out;
8587 }
8588 }
8589 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008590 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008591 popstackmark(&smark);
8592}
8593
Eric Andersenc470f442003-07-28 09:56:35 +00008594/*
8595 * Kick off a subshell to evaluate a tree.
8596 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008597static void
Eric Andersenc470f442003-07-28 09:56:35 +00008598evalsubshell(union node *n, int flags)
8599{
8600 struct job *jp;
8601 int backgnd = (n->type == NBACKGND);
8602 int status;
8603
8604 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008605 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008606 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008607 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008608 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008609 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008610 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008611 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008612 flags |= EV_EXIT;
8613 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008614 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008615 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008616 redirect(n->nredir.redirect, 0);
8617 evaltreenr(n->nredir.n, flags);
8618 /* never returns */
8619 }
8620 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008621 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008622 status = waitforjob(jp);
8623 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008624 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008625}
8626
Eric Andersenc470f442003-07-28 09:56:35 +00008627/*
8628 * Compute the names of the files in a redirection list.
8629 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008630static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008631static void
8632expredir(union node *n)
8633{
8634 union node *redir;
8635
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008636 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008637 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008638
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008639 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008640 fn.lastp = &fn.list;
8641 switch (redir->type) {
8642 case NFROMTO:
8643 case NFROM:
8644 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008645#if ENABLE_ASH_BASH_COMPAT
8646 case NTO2:
8647#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008648 case NCLOBBER:
8649 case NAPPEND:
8650 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008651#if ENABLE_ASH_BASH_COMPAT
8652 store_expfname:
8653#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008654 redir->nfile.expfname = fn.list->text;
8655 break;
8656 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008657 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008658 if (redir->ndup.vname) {
8659 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008660 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008661 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008662#if ENABLE_ASH_BASH_COMPAT
8663//FIXME: we used expandarg with different args!
8664 if (!isdigit_str9(fn.list->text)) {
8665 /* >&file, not >&fd */
8666 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8667 ash_msg_and_raise_error("redir error");
8668 redir->type = NTO2;
8669 goto store_expfname;
8670 }
8671#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008672 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008673 }
8674 break;
8675 }
8676 }
8677}
8678
Eric Andersencb57d552001-06-28 07:25:16 +00008679/*
Eric Andersencb57d552001-06-28 07:25:16 +00008680 * Evaluate a pipeline. All the processes in the pipeline are children
8681 * of the process creating the pipeline. (This differs from some versions
8682 * of the shell, which make the last process in a pipeline the parent
8683 * of all the rest.)
8684 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008685static void
Eric Andersenc470f442003-07-28 09:56:35 +00008686evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008687{
8688 struct job *jp;
8689 struct nodelist *lp;
8690 int pipelen;
8691 int prevfd;
8692 int pip[2];
8693
Eric Andersenc470f442003-07-28 09:56:35 +00008694 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008695 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008696 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008697 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008698 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008699 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008700 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008701 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008702 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008703 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008704 pip[1] = -1;
8705 if (lp->next) {
8706 if (pipe(pip) < 0) {
8707 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008708 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008709 }
8710 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008711 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008712 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008713 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008714 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008715 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008716 if (prevfd > 0) {
8717 dup2(prevfd, 0);
8718 close(prevfd);
8719 }
8720 if (pip[1] > 1) {
8721 dup2(pip[1], 1);
8722 close(pip[1]);
8723 }
Eric Andersenc470f442003-07-28 09:56:35 +00008724 evaltreenr(lp->n, flags);
8725 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008726 }
8727 if (prevfd >= 0)
8728 close(prevfd);
8729 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008730 /* Don't want to trigger debugging */
8731 if (pip[1] != -1)
8732 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008733 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008734 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008735 exitstatus = waitforjob(jp);
8736 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008737 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008738 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008739}
8740
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008741/*
8742 * Controls whether the shell is interactive or not.
8743 */
8744static void
8745setinteractive(int on)
8746{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008747 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008748
8749 if (++on == is_interactive)
8750 return;
8751 is_interactive = on;
8752 setsignal(SIGINT);
8753 setsignal(SIGQUIT);
8754 setsignal(SIGTERM);
8755#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8756 if (is_interactive > 1) {
8757 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008758 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008759
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008760 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008761 /* note: ash and hush share this string */
8762 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008763 "Enter 'help' for a list of built-in commands."
8764 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008765 bb_banner,
8766 "built-in shell (ash)"
8767 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008768 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008769 }
8770 }
8771#endif
8772}
8773
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008774static void
8775optschanged(void)
8776{
8777#if DEBUG
8778 opentrace();
8779#endif
8780 setinteractive(iflag);
8781 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008782#if ENABLE_FEATURE_EDITING_VI
8783 if (viflag)
8784 line_input_state->flags |= VI_MODE;
8785 else
8786 line_input_state->flags &= ~VI_MODE;
8787#else
8788 viflag = 0; /* forcibly keep the option off */
8789#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008790}
8791
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008792static struct localvar *localvars;
8793
8794/*
8795 * Called after a function returns.
8796 * Interrupts must be off.
8797 */
8798static void
8799poplocalvars(void)
8800{
8801 struct localvar *lvp;
8802 struct var *vp;
8803
8804 while ((lvp = localvars) != NULL) {
8805 localvars = lvp->next;
8806 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008807 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008808 if (vp == NULL) { /* $- saved */
8809 memcpy(optlist, lvp->text, sizeof(optlist));
8810 free((char*)lvp->text);
8811 optschanged();
8812 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008813 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008814 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008815 if (vp->var_func)
8816 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008817 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008818 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008819 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008820 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008821 }
8822 free(lvp);
8823 }
8824}
8825
8826static int
8827evalfun(struct funcnode *func, int argc, char **argv, int flags)
8828{
8829 volatile struct shparam saveparam;
8830 struct localvar *volatile savelocalvars;
8831 struct jmploc *volatile savehandler;
8832 struct jmploc jmploc;
8833 int e;
8834
8835 saveparam = shellparam;
8836 savelocalvars = localvars;
8837 e = setjmp(jmploc.loc);
8838 if (e) {
8839 goto funcdone;
8840 }
8841 INT_OFF;
8842 savehandler = exception_handler;
8843 exception_handler = &jmploc;
8844 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008845 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008846 func->count++;
8847 funcnest++;
8848 INT_ON;
8849 shellparam.nparam = argc - 1;
8850 shellparam.p = argv + 1;
8851#if ENABLE_ASH_GETOPTS
8852 shellparam.optind = 1;
8853 shellparam.optoff = -1;
8854#endif
8855 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008856 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008857 INT_OFF;
8858 funcnest--;
8859 freefunc(func);
8860 poplocalvars();
8861 localvars = savelocalvars;
8862 freeparam(&shellparam);
8863 shellparam = saveparam;
8864 exception_handler = savehandler;
8865 INT_ON;
8866 evalskip &= ~SKIPFUNC;
8867 return e;
8868}
8869
Denis Vlasenko131ae172007-02-18 13:00:19 +00008870#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008871static char **
8872parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008873{
8874 char *cp, c;
8875
8876 for (;;) {
8877 cp = *++argv;
8878 if (!cp)
8879 return 0;
8880 if (*cp++ != '-')
8881 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008882 c = *cp++;
8883 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008884 break;
8885 if (c == '-' && !*cp) {
8886 argv++;
8887 break;
8888 }
8889 do {
8890 switch (c) {
8891 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008892 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008893 break;
8894 default:
8895 /* run 'typecmd' for other options */
8896 return 0;
8897 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008898 c = *cp++;
8899 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008900 }
8901 return argv;
8902}
8903#endif
8904
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008905/*
8906 * Make a variable a local variable. When a variable is made local, it's
8907 * value and flags are saved in a localvar structure. The saved values
8908 * will be restored when the shell function returns. We handle the name
8909 * "-" as a special case.
8910 */
8911static void
8912mklocal(char *name)
8913{
8914 struct localvar *lvp;
8915 struct var **vpp;
8916 struct var *vp;
8917
8918 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008919 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008920 if (LONE_DASH(name)) {
8921 char *p;
8922 p = ckmalloc(sizeof(optlist));
8923 lvp->text = memcpy(p, optlist, sizeof(optlist));
8924 vp = NULL;
8925 } else {
8926 char *eq;
8927
8928 vpp = hashvar(name);
8929 vp = *findvar(vpp, name);
8930 eq = strchr(name, '=');
8931 if (vp == NULL) {
8932 if (eq)
8933 setvareq(name, VSTRFIXED);
8934 else
8935 setvar(name, NULL, VSTRFIXED);
8936 vp = *vpp; /* the new variable */
8937 lvp->flags = VUNSET;
8938 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008939 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008940 lvp->flags = vp->flags;
8941 vp->flags |= VSTRFIXED|VTEXTFIXED;
8942 if (eq)
8943 setvareq(name, 0);
8944 }
8945 }
8946 lvp->vp = vp;
8947 lvp->next = localvars;
8948 localvars = lvp;
8949 INT_ON;
8950}
8951
8952/*
8953 * The "local" command.
8954 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008955static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008956localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008957{
8958 char *name;
8959
8960 argv = argptr;
8961 while ((name = *argv++) != NULL) {
8962 mklocal(name);
8963 }
8964 return 0;
8965}
8966
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008967static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008968falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008969{
8970 return 1;
8971}
8972
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008973static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008974truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008975{
8976 return 0;
8977}
8978
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008979static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008980execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008981{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008982 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008983 iflag = 0; /* exit on error */
8984 mflag = 0;
8985 optschanged();
8986 shellexec(argv + 1, pathval(), 0);
8987 }
8988 return 0;
8989}
8990
8991/*
8992 * The return command.
8993 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008994static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008995returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008996{
8997 /*
8998 * If called outside a function, do what ksh does;
8999 * skip the rest of the file.
9000 */
9001 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9002 return argv[1] ? number(argv[1]) : exitstatus;
9003}
9004
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009005/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009006static int breakcmd(int, char **) FAST_FUNC;
9007static int dotcmd(int, char **) FAST_FUNC;
9008static int evalcmd(int, char **) FAST_FUNC;
9009static int exitcmd(int, char **) FAST_FUNC;
9010static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009011#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009012static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009013#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009014#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009015static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009016#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009017#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009018static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009019#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009020static int readcmd(int, char **) FAST_FUNC;
9021static int setcmd(int, char **) FAST_FUNC;
9022static int shiftcmd(int, char **) FAST_FUNC;
9023static int timescmd(int, char **) FAST_FUNC;
9024static int trapcmd(int, char **) FAST_FUNC;
9025static int umaskcmd(int, char **) FAST_FUNC;
9026static int unsetcmd(int, char **) FAST_FUNC;
9027static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009028
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009029#define BUILTIN_NOSPEC "0"
9030#define BUILTIN_SPECIAL "1"
9031#define BUILTIN_REGULAR "2"
9032#define BUILTIN_SPEC_REG "3"
9033#define BUILTIN_ASSIGN "4"
9034#define BUILTIN_SPEC_ASSG "5"
9035#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009036#define BUILTIN_SPEC_REG_ASSG "7"
9037
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009038/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009039#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009040static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009041#endif
9042#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009043static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009044#endif
9045#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009046static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009047#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009048
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009049/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009050static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009051 { BUILTIN_SPEC_REG "." , dotcmd },
9052 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009053#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009054 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009055#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009056 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009057#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009058#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009059#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009060 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009061#endif
9062#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009063 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009064#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009065 { BUILTIN_SPEC_REG "break" , breakcmd },
9066 { BUILTIN_REGULAR "cd" , cdcmd },
9067 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009068#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009069 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009072#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009073 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009074#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009075 { BUILTIN_SPEC_REG "eval" , evalcmd },
9076 { BUILTIN_SPEC_REG "exec" , execcmd },
9077 { BUILTIN_SPEC_REG "exit" , exitcmd },
9078 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9079 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009080#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009081 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009082#endif
9083#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009084 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009085#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009086 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009087#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009088 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009089#endif
9090#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009091 { BUILTIN_REGULAR "jobs" , jobscmd },
9092 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009093#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009094#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009095 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009096#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009097 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009098#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009099 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009100#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009101 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9102 { BUILTIN_REGULAR "read" , readcmd },
9103 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9104 { BUILTIN_SPEC_REG "return" , returncmd },
9105 { BUILTIN_SPEC_REG "set" , setcmd },
9106 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009107#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009108 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009109#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009110#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009111 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009112#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009113 { BUILTIN_SPEC_REG "times" , timescmd },
9114 { BUILTIN_SPEC_REG "trap" , trapcmd },
9115 { BUILTIN_REGULAR "true" , truecmd },
9116 { BUILTIN_NOSPEC "type" , typecmd },
9117 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9118 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009119#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009120 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009121#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009122 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9123 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009124};
9125
Denis Vlasenko80591b02008-03-25 07:49:43 +00009126/* Should match the above table! */
9127#define COMMANDCMD (builtintab + \
9128 2 + \
9129 1 * ENABLE_ASH_BUILTIN_TEST + \
9130 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9131 1 * ENABLE_ASH_ALIAS + \
9132 1 * ENABLE_ASH_JOB_CONTROL + \
9133 3)
9134#define EXECCMD (builtintab + \
9135 2 + \
9136 1 * ENABLE_ASH_BUILTIN_TEST + \
9137 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9138 1 * ENABLE_ASH_ALIAS + \
9139 1 * ENABLE_ASH_JOB_CONTROL + \
9140 3 + \
9141 1 * ENABLE_ASH_CMDCMD + \
9142 1 + \
9143 ENABLE_ASH_BUILTIN_ECHO + \
9144 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009145
9146/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009147 * Search the table of builtin commands.
9148 */
9149static struct builtincmd *
9150find_builtin(const char *name)
9151{
9152 struct builtincmd *bp;
9153
9154 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009155 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009156 pstrcmp
9157 );
9158 return bp;
9159}
9160
9161/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009162 * Execute a simple command.
9163 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009164static int
9165isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009166{
9167 const char *q = endofname(p);
9168 if (p == q)
9169 return 0;
9170 return *q == '=';
9171}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009172static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009173bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009174{
9175 /* Preserve exitstatus of a previous possible redirection
9176 * as POSIX mandates */
9177 return back_exitstatus;
9178}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009179static void
Eric Andersenc470f442003-07-28 09:56:35 +00009180evalcommand(union node *cmd, int flags)
9181{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009182 static const struct builtincmd null_bltin = {
9183 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009184 };
Eric Andersenc470f442003-07-28 09:56:35 +00009185 struct stackmark smark;
9186 union node *argp;
9187 struct arglist arglist;
9188 struct arglist varlist;
9189 char **argv;
9190 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009191 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009192 struct cmdentry cmdentry;
9193 struct job *jp;
9194 char *lastarg;
9195 const char *path;
9196 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009197 int status;
9198 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009199 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009200 smallint cmd_is_exec;
9201 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009202
9203 /* First expand the arguments. */
9204 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9205 setstackmark(&smark);
9206 back_exitstatus = 0;
9207
9208 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009209 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009210 varlist.lastp = &varlist.list;
9211 *varlist.lastp = NULL;
9212 arglist.lastp = &arglist.list;
9213 *arglist.lastp = NULL;
9214
9215 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009216 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009217 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9218 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9219 }
9220
Eric Andersenc470f442003-07-28 09:56:35 +00009221 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9222 struct strlist **spp;
9223
9224 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009225 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009226 expandarg(argp, &arglist, EXP_VARTILDE);
9227 else
9228 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9229
Eric Andersenc470f442003-07-28 09:56:35 +00009230 for (sp = *spp; sp; sp = sp->next)
9231 argc++;
9232 }
9233
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009234 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009235 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009236 TRACE(("evalcommand arg: %s\n", sp->text));
9237 *nargv++ = sp->text;
9238 }
9239 *nargv = NULL;
9240
9241 lastarg = NULL;
9242 if (iflag && funcnest == 0 && argc > 0)
9243 lastarg = nargv[-1];
9244
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009245 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009246 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009247 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009248
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009249 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009250 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9251 struct strlist **spp;
9252 char *p;
9253
9254 spp = varlist.lastp;
9255 expandarg(argp, &varlist, EXP_VARTILDE);
9256
9257 /*
9258 * Modify the command lookup path, if a PATH= assignment
9259 * is present
9260 */
9261 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009262 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009263 path = p;
9264 }
9265
9266 /* Print the command if xflag is set. */
9267 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009268 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009269 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009270
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009271 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009272 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009273 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009274 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009275 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009276 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009277 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009278 }
9279 sp = arglist.list;
9280 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009281 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009282 }
9283
9284 cmd_is_exec = 0;
9285 spclbltin = -1;
9286
9287 /* Now locate the command. */
9288 if (argc) {
9289 const char *oldpath;
9290 int cmd_flag = DO_ERR;
9291
9292 path += 5;
9293 oldpath = path;
9294 for (;;) {
9295 find_command(argv[0], &cmdentry, cmd_flag, path);
9296 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009297 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009298 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009299 goto bail;
9300 }
9301
9302 /* implement bltin and command here */
9303 if (cmdentry.cmdtype != CMDBUILTIN)
9304 break;
9305 if (spclbltin < 0)
9306 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9307 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009308 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009309#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009310 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009311 path = oldpath;
9312 nargv = parse_command_args(argv, &path);
9313 if (!nargv)
9314 break;
9315 argc -= nargv - argv;
9316 argv = nargv;
9317 cmd_flag |= DO_NOFUNC;
9318 } else
9319#endif
9320 break;
9321 }
9322 }
9323
9324 if (status) {
9325 /* We have a redirection error. */
9326 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009327 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009328 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009329 exitstatus = status;
9330 goto out;
9331 }
9332
9333 /* Execute the command. */
9334 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009335 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009336
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009337#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009338/* (1) BUG: if variables are set, we need to fork, or save/restore them
9339 * around run_nofork_applet() call.
9340 * (2) Should this check also be done in forkshell()?
9341 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9342 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009343 /* find_command() encodes applet_no as (-2 - applet_no) */
9344 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009345 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009346 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009347 /* run <applet>_main() */
9348 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009349 break;
9350 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009351#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009352 /* Can we avoid forking off? For example, very last command
9353 * in a script or a subshell does not need forking,
9354 * we can just exec it.
9355 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009356 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009357 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009358 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009359 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009360 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009361 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009362 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009363 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009364 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009365 break;
9366 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009367 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009368 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009369 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009370 }
9371 listsetvar(varlist.list, VEXPORT|VSTACK);
9372 shellexec(argv, path, cmdentry.u.index);
9373 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009374 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009375 case CMDBUILTIN:
9376 cmdenviron = varlist.list;
9377 if (cmdenviron) {
9378 struct strlist *list = cmdenviron;
9379 int i = VNOSET;
9380 if (spclbltin > 0 || argc == 0) {
9381 i = 0;
9382 if (cmd_is_exec && argc > 1)
9383 i = VEXPORT;
9384 }
9385 listsetvar(list, i);
9386 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009387 /* Tight loop with builtins only:
9388 * "while kill -0 $child; do true; done"
9389 * will never exit even if $child died, unless we do this
9390 * to reap the zombie and make kill detect that it's gone: */
9391 dowait(DOWAIT_NONBLOCK, NULL);
9392
Eric Andersenc470f442003-07-28 09:56:35 +00009393 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9394 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009395 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009396 if (i == EXEXIT)
9397 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009398 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009399 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009400 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009401 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009402 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009403 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009404 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009405 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009406 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009407 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009408 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009409 }
9410 break;
9411
9412 case CMDFUNCTION:
9413 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009414 /* See above for the rationale */
9415 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009416 if (evalfun(cmdentry.u.func, argc, argv, flags))
9417 goto raise;
9418 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009419
9420 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009421
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009422 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009423 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009424 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009425 /* dsl: I think this is intended to be used to support
9426 * '_' in 'vi' command mode during line editing...
9427 * However I implemented that within libedit itself.
9428 */
9429 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009430 }
Eric Andersenc470f442003-07-28 09:56:35 +00009431 popstackmark(&smark);
9432}
9433
9434static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009435evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9436{
Eric Andersenc470f442003-07-28 09:56:35 +00009437 char *volatile savecmdname;
9438 struct jmploc *volatile savehandler;
9439 struct jmploc jmploc;
9440 int i;
9441
9442 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009443 i = setjmp(jmploc.loc);
9444 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009445 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009446 savehandler = exception_handler;
9447 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009448 commandname = argv[0];
9449 argptr = argv + 1;
9450 optptr = NULL; /* initialize nextopt */
9451 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009452 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009453 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009454 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009455 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009456 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009457 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009458
9459 return i;
9460}
9461
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009462static int
9463goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009464{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009465 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009466}
9467
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009468
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009469/*
9470 * Search for a command. This is called before we fork so that the
9471 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009472 * the child. The check for "goodname" is an overly conservative
9473 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009474 */
Eric Andersenc470f442003-07-28 09:56:35 +00009475static void
9476prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009477{
9478 struct cmdentry entry;
9479
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009480 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9481 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009482}
9483
Eric Andersencb57d552001-06-28 07:25:16 +00009484
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009485/* ============ Builtin commands
9486 *
9487 * Builtin commands whose functions are closely tied to evaluation
9488 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009489 */
9490
9491/*
Eric Andersencb57d552001-06-28 07:25:16 +00009492 * Handle break and continue commands. Break, continue, and return are
9493 * all handled by setting the evalskip flag. The evaluation routines
9494 * above all check this flag, and if it is set they start skipping
9495 * commands rather than executing them. The variable skipcount is
9496 * the number of loops to break/continue, or the number of function
9497 * levels to return. (The latter is always 1.) It should probably
9498 * be an error to break out of more loops than exist, but it isn't
9499 * in the standard shell so we don't make it one here.
9500 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009501static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009502breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009503{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009504 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009505
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009506 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009507 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009508 if (n > loopnest)
9509 n = loopnest;
9510 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009511 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009512 skipcount = n;
9513 }
9514 return 0;
9515}
9516
Eric Andersenc470f442003-07-28 09:56:35 +00009517
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009518/* ============ input.c
9519 *
Eric Andersen90898442003-08-06 11:20:52 +00009520 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009521 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009522
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009523enum {
9524 INPUT_PUSH_FILE = 1,
9525 INPUT_NOFILE_OK = 2,
9526};
Eric Andersencb57d552001-06-28 07:25:16 +00009527
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009528static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009529/* values of checkkwd variable */
9530#define CHKALIAS 0x1
9531#define CHKKWD 0x2
9532#define CHKNL 0x4
9533
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009534/*
9535 * Push a string back onto the input at this current parsefile level.
9536 * We handle aliases this way.
9537 */
9538#if !ENABLE_ASH_ALIAS
9539#define pushstring(s, ap) pushstring(s)
9540#endif
9541static void
9542pushstring(char *s, struct alias *ap)
9543{
9544 struct strpush *sp;
9545 int len;
9546
9547 len = strlen(s);
9548 INT_OFF;
9549 if (g_parsefile->strpush) {
9550 sp = ckzalloc(sizeof(*sp));
9551 sp->prev = g_parsefile->strpush;
9552 } else {
9553 sp = &(g_parsefile->basestrpush);
9554 }
9555 g_parsefile->strpush = sp;
9556 sp->prev_string = g_parsefile->next_to_pgetc;
9557 sp->prev_left_in_line = g_parsefile->left_in_line;
9558#if ENABLE_ASH_ALIAS
9559 sp->ap = ap;
9560 if (ap) {
9561 ap->flag |= ALIASINUSE;
9562 sp->string = s;
9563 }
9564#endif
9565 g_parsefile->next_to_pgetc = s;
9566 g_parsefile->left_in_line = len;
9567 INT_ON;
9568}
9569
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009570static void
9571popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009572{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009573 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009574
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009575 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009576#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009577 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009578 if (g_parsefile->next_to_pgetc[-1] == ' '
9579 || g_parsefile->next_to_pgetc[-1] == '\t'
9580 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009581 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009582 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009583 if (sp->string != sp->ap->val) {
9584 free(sp->string);
9585 }
9586 sp->ap->flag &= ~ALIASINUSE;
9587 if (sp->ap->flag & ALIASDEAD) {
9588 unalias(sp->ap->name);
9589 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009590 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009591#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009592 g_parsefile->next_to_pgetc = sp->prev_string;
9593 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009594 g_parsefile->strpush = sp->prev;
9595 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009596 free(sp);
9597 INT_ON;
9598}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009599
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009600//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9601//it peeks whether it is &>, and then pushes back both chars.
9602//This function needs to save last *next_to_pgetc to buf[0]
9603//to make two pungetc() reliable. Currently,
9604// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009605static int
9606preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009607{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009608 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009609 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009610
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009611 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009612#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009613 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009614 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9615 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009616 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009617 int timeout = -1;
9618# if ENABLE_ASH_IDLE_TIMEOUT
9619 if (iflag) {
9620 const char *tmout_var = lookupvar("TMOUT");
9621 if (tmout_var) {
9622 timeout = atoi(tmout_var) * 1000;
9623 if (timeout <= 0)
9624 timeout = -1;
9625 }
9626 }
9627# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009628# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009629 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009630# endif
Denys Vlasenko20704f02011-03-23 17:59:27 +01009631 /* Unicode support should be activated even if LANG is set
9632 * _during_ shell execution, not only if it was set when
9633 * shell was started. Therefore, re-check LANG every time:
9634 */
9635 reinit_unicode(lookupvar("LANG"));
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009636 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009637 if (nr == 0) {
9638 /* Ctrl+C pressed */
9639 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009640 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009641 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009642 raise(SIGINT);
9643 return 1;
9644 }
Eric Andersenc470f442003-07-28 09:56:35 +00009645 goto retry;
9646 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009647 if (nr < 0) {
9648 if (errno == 0) {
9649 /* Ctrl+D pressed */
9650 nr = 0;
9651 }
9652# if ENABLE_ASH_IDLE_TIMEOUT
9653 else if (errno == EAGAIN && timeout > 0) {
9654 printf("\007timed out waiting for input: auto-logout\n");
9655 exitshell();
9656 }
9657# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009658 }
Eric Andersencb57d552001-06-28 07:25:16 +00009659 }
9660#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009661 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009662#endif
9663
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009664#if 0 /* disabled: nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009665 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009666 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009667 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009668 if (flags >= 0 && (flags & O_NONBLOCK)) {
9669 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009670 if (fcntl(0, F_SETFL, flags) >= 0) {
9671 out2str("sh: turning off NDELAY mode\n");
9672 goto retry;
9673 }
9674 }
9675 }
9676 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009677#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009678 return nr;
9679}
9680
9681/*
9682 * Refill the input buffer and return the next input character:
9683 *
9684 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009685 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9686 * or we are reading from a string so we can't refill the buffer,
9687 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009688 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009689 * 4) Process input up to the next newline, deleting nul characters.
9690 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009691//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9692#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009693static int
Eric Andersenc470f442003-07-28 09:56:35 +00009694preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009695{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009696 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009697 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009698
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009699 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009700#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009701 if (g_parsefile->left_in_line == -1
9702 && g_parsefile->strpush->ap
9703 && g_parsefile->next_to_pgetc[-1] != ' '
9704 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009705 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009706 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009707 return PEOA;
9708 }
Eric Andersen2870d962001-07-02 17:27:21 +00009709#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009710 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009711 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009712 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9713 g_parsefile->left_in_line,
9714 g_parsefile->next_to_pgetc,
9715 g_parsefile->next_to_pgetc);
9716 if (--g_parsefile->left_in_line >= 0)
9717 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009718 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009719 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009720 * "pgetc" needs refilling.
9721 */
9722
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009723 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009724 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009725 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009726 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009727 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009728 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009729 /* even in failure keep left_in_line and next_to_pgetc
9730 * in lock step, for correct multi-layer pungetc.
9731 * left_in_line was decremented before preadbuffer(),
9732 * must inc next_to_pgetc: */
9733 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009734 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009735 }
Eric Andersencb57d552001-06-28 07:25:16 +00009736
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009737 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009738 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009739 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009740 again:
9741 more = preadfd();
9742 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009743 /* don't try reading again */
9744 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009745 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009746 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009747 return PEOF;
9748 }
9749 }
9750
Denis Vlasenko727752d2008-11-28 03:41:47 +00009751 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009752 * Set g_parsefile->left_in_line
9753 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009754 * NUL chars are deleted.
9755 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009756 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009757 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009758 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009759
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009760 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009761
Denis Vlasenko727752d2008-11-28 03:41:47 +00009762 c = *q;
9763 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009764 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009765 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009766 q++;
9767 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009768 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009769 break;
9770 }
Eric Andersencb57d552001-06-28 07:25:16 +00009771 }
9772
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009773 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009774 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9775 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009776 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009777 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009778 }
9779 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009780 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009781
Eric Andersencb57d552001-06-28 07:25:16 +00009782 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009783 char save = *q;
9784 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009785 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009786 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009787 }
9788
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009789 pgetc_debug("preadbuffer at %d:%p'%s'",
9790 g_parsefile->left_in_line,
9791 g_parsefile->next_to_pgetc,
9792 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009793 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009794}
9795
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009796#define pgetc_as_macro() \
9797 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009798 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009799 : preadbuffer() \
9800 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009801
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009802static int
9803pgetc(void)
9804{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009805 pgetc_debug("pgetc_fast at %d:%p'%s'",
9806 g_parsefile->left_in_line,
9807 g_parsefile->next_to_pgetc,
9808 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009809 return pgetc_as_macro();
9810}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009811
9812#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009813# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009814#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009815# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009816#endif
9817
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009818#if ENABLE_ASH_ALIAS
9819static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009820pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009821{
9822 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009823 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009824 pgetc_debug("pgetc_fast at %d:%p'%s'",
9825 g_parsefile->left_in_line,
9826 g_parsefile->next_to_pgetc,
9827 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009828 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009829 } while (c == PEOA);
9830 return c;
9831}
9832#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009833# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009834#endif
9835
9836/*
9837 * Read a line from the script.
9838 */
9839static char *
9840pfgets(char *line, int len)
9841{
9842 char *p = line;
9843 int nleft = len;
9844 int c;
9845
9846 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009847 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009848 if (c == PEOF) {
9849 if (p == line)
9850 return NULL;
9851 break;
9852 }
9853 *p++ = c;
9854 if (c == '\n')
9855 break;
9856 }
9857 *p = '\0';
9858 return line;
9859}
9860
Eric Andersenc470f442003-07-28 09:56:35 +00009861/*
9862 * Undo the last call to pgetc. Only one character may be pushed back.
9863 * PEOF may be pushed back.
9864 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009865static void
Eric Andersenc470f442003-07-28 09:56:35 +00009866pungetc(void)
9867{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009868 g_parsefile->left_in_line++;
9869 g_parsefile->next_to_pgetc--;
9870 pgetc_debug("pushed back to %d:%p'%s'",
9871 g_parsefile->left_in_line,
9872 g_parsefile->next_to_pgetc,
9873 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009874}
9875
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009876/*
9877 * To handle the "." command, a stack of input files is used. Pushfile
9878 * adds a new entry to the stack and popfile restores the previous level.
9879 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009880static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009881pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009882{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009883 struct parsefile *pf;
9884
Denis Vlasenko597906c2008-02-20 16:38:54 +00009885 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009886 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009887 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009888 /*pf->strpush = NULL; - ckzalloc did it */
9889 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009890 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009891}
9892
9893static void
9894popfile(void)
9895{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009896 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009897
Denis Vlasenkob012b102007-02-19 22:43:01 +00009898 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009899 if (pf->pf_fd >= 0)
9900 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009901 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009902 while (pf->strpush)
9903 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009904 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009905 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009906 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009907}
9908
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009909/*
9910 * Return to top level.
9911 */
9912static void
9913popallfiles(void)
9914{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009915 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009916 popfile();
9917}
9918
9919/*
9920 * Close the file(s) that the shell is reading commands from. Called
9921 * after a fork is done.
9922 */
9923static void
9924closescript(void)
9925{
9926 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009927 if (g_parsefile->pf_fd > 0) {
9928 close(g_parsefile->pf_fd);
9929 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009930 }
9931}
9932
9933/*
9934 * Like setinputfile, but takes an open file descriptor. Call this with
9935 * interrupts off.
9936 */
9937static void
9938setinputfd(int fd, int push)
9939{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009940 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009941 if (push) {
9942 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009943 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009944 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009945 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009946 if (g_parsefile->buf == NULL)
9947 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009948 g_parsefile->left_in_buffer = 0;
9949 g_parsefile->left_in_line = 0;
9950 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009951}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009952
Eric Andersenc470f442003-07-28 09:56:35 +00009953/*
9954 * Set the input to take input from a file. If push is set, push the
9955 * old input onto the stack first.
9956 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009957static int
9958setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009959{
9960 int fd;
9961 int fd2;
9962
Denis Vlasenkob012b102007-02-19 22:43:01 +00009963 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009964 fd = open(fname, O_RDONLY);
9965 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009966 if (flags & INPUT_NOFILE_OK)
9967 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009968 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009969 }
Eric Andersenc470f442003-07-28 09:56:35 +00009970 if (fd < 10) {
9971 fd2 = copyfd(fd, 10);
9972 close(fd);
9973 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009974 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009975 fd = fd2;
9976 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009977 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009978 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009979 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009980 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009981}
9982
Eric Andersencb57d552001-06-28 07:25:16 +00009983/*
9984 * Like setinputfile, but takes input from a string.
9985 */
Eric Andersenc470f442003-07-28 09:56:35 +00009986static void
9987setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009988{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009989 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009990 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009991 g_parsefile->next_to_pgetc = string;
9992 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009993 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009994 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009995 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009996}
9997
9998
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009999/* ============ mail.c
10000 *
10001 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000010002 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010003
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010004#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000010005
Eric Andersencb57d552001-06-28 07:25:16 +000010006#define MAXMBOXES 10
10007
Eric Andersenc470f442003-07-28 09:56:35 +000010008/* times of mailboxes */
10009static time_t mailtime[MAXMBOXES];
10010/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010011static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010012
Eric Andersencb57d552001-06-28 07:25:16 +000010013/*
Eric Andersenc470f442003-07-28 09:56:35 +000010014 * Print appropriate message(s) if mail has arrived.
10015 * If mail_var_path_changed is set,
10016 * then the value of MAIL has mail_var_path_changed,
10017 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010018 */
Eric Andersenc470f442003-07-28 09:56:35 +000010019static void
10020chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010021{
Eric Andersencb57d552001-06-28 07:25:16 +000010022 const char *mpath;
10023 char *p;
10024 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010025 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010026 struct stackmark smark;
10027 struct stat statb;
10028
Eric Andersencb57d552001-06-28 07:25:16 +000010029 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010030 mpath = mpathset() ? mpathval() : mailval();
10031 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010032 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010033 if (p == NULL)
10034 break;
10035 if (*p == '\0')
10036 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010037 for (q = p; *q; q++)
10038 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010039#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010040 if (q[-1] != '/')
10041 abort();
10042#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010043 q[-1] = '\0'; /* delete trailing '/' */
10044 if (stat(p, &statb) < 0) {
10045 *mtp = 0;
10046 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010047 }
Eric Andersenc470f442003-07-28 09:56:35 +000010048 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10049 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010050 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010051 pathopt ? pathopt : "you have mail"
10052 );
10053 }
10054 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010055 }
Eric Andersenc470f442003-07-28 09:56:35 +000010056 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010057 popstackmark(&smark);
10058}
Eric Andersencb57d552001-06-28 07:25:16 +000010059
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010060static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010061changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010062{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010063 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010064}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010065
Denis Vlasenko131ae172007-02-18 13:00:19 +000010066#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010067
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010068
10069/* ============ ??? */
10070
Eric Andersencb57d552001-06-28 07:25:16 +000010071/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010072 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010073 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010074static void
10075setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010076{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010077 char **newparam;
10078 char **ap;
10079 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010080
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010081 for (nparam = 0; argv[nparam]; nparam++)
10082 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010083 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10084 while (*argv) {
10085 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010086 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010087 *ap = NULL;
10088 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010089 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010090 shellparam.nparam = nparam;
10091 shellparam.p = newparam;
10092#if ENABLE_ASH_GETOPTS
10093 shellparam.optind = 1;
10094 shellparam.optoff = -1;
10095#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010096}
10097
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010098/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010099 * Process shell options. The global variable argptr contains a pointer
10100 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010101 *
10102 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10103 * For a non-interactive shell, an error condition encountered
10104 * by a special built-in ... shall cause the shell to write a diagnostic message
10105 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010106 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010107 * ...
10108 * Utility syntax error (option or operand error) Shall exit
10109 * ...
10110 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10111 * we see that bash does not do that (set "finishes" with error code 1 instead,
10112 * and shell continues), and people rely on this behavior!
10113 * Testcase:
10114 * set -o barfoo 2>/dev/null
10115 * echo $?
10116 *
10117 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010118 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010119static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010120plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010121{
10122 int i;
10123
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010124 if (name) {
10125 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010126 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010127 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010128 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010129 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010130 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010131 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010132 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010133 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010134 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010135 if (val) {
10136 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10137 } else {
10138 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10139 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010140 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010141 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010142}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010143static void
10144setoption(int flag, int val)
10145{
10146 int i;
10147
10148 for (i = 0; i < NOPTS; i++) {
10149 if (optletters(i) == flag) {
10150 optlist[i] = val;
10151 return;
10152 }
10153 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010154 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010155 /* NOTREACHED */
10156}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010157static int
Eric Andersenc470f442003-07-28 09:56:35 +000010158options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010159{
10160 char *p;
10161 int val;
10162 int c;
10163
10164 if (cmdline)
10165 minusc = NULL;
10166 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010167 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010168 if (c != '-' && c != '+')
10169 break;
10170 argptr++;
10171 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010172 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010173 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010174 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010175 if (!cmdline) {
10176 /* "-" means turn off -x and -v */
10177 if (p[0] == '\0')
10178 xflag = vflag = 0;
10179 /* "--" means reset params */
10180 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010181 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010182 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010183 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010184 }
Eric Andersencb57d552001-06-28 07:25:16 +000010185 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010186 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010187 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010188 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010189 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010190 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010191 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010192 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010193 /* it already printed err message */
10194 return 1; /* error */
10195 }
Eric Andersencb57d552001-06-28 07:25:16 +000010196 if (*argptr)
10197 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010198 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10199 isloginsh = 1;
10200 /* bash does not accept +-login, we also won't */
10201 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010202 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010203 isloginsh = 1;
10204 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010205 } else {
10206 setoption(c, val);
10207 }
10208 }
10209 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010210 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010211}
10212
Eric Andersencb57d552001-06-28 07:25:16 +000010213/*
Eric Andersencb57d552001-06-28 07:25:16 +000010214 * The shift builtin command.
10215 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010216static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010217shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010218{
10219 int n;
10220 char **ap1, **ap2;
10221
10222 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010223 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010224 n = number(argv[1]);
10225 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010226 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010227 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010228 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010229 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010230 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010231 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010232 }
10233 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010234 while ((*ap2++ = *ap1++) != NULL)
10235 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010236#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010237 shellparam.optind = 1;
10238 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010239#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010240 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010241 return 0;
10242}
10243
Eric Andersencb57d552001-06-28 07:25:16 +000010244/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010245 * POSIX requires that 'set' (but not export or readonly) output the
10246 * variables in lexicographic order - by the locale's collating order (sigh).
10247 * Maybe we could keep them in an ordered balanced binary tree
10248 * instead of hashed lists.
10249 * For now just roll 'em through qsort for printing...
10250 */
10251static int
10252showvars(const char *sep_prefix, int on, int off)
10253{
10254 const char *sep;
10255 char **ep, **epend;
10256
10257 ep = listvars(on, off, &epend);
10258 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10259
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010260 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010261
10262 for (; ep < epend; ep++) {
10263 const char *p;
10264 const char *q;
10265
10266 p = strchrnul(*ep, '=');
10267 q = nullstr;
10268 if (*p)
10269 q = single_quote(++p);
10270 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10271 }
10272 return 0;
10273}
10274
10275/*
Eric Andersencb57d552001-06-28 07:25:16 +000010276 * The set command builtin.
10277 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010278static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010279setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010280{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010281 int retval;
10282
Denis Vlasenko68404f12008-03-17 09:00:54 +000010283 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010284 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010285
Denis Vlasenkob012b102007-02-19 22:43:01 +000010286 INT_OFF;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010287 retval = options(/*cmdline:*/ 0);
10288 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010289 optschanged();
10290 if (*argptr != NULL) {
10291 setparam(argptr);
10292 }
Eric Andersencb57d552001-06-28 07:25:16 +000010293 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010294 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010295 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010296}
10297
Denis Vlasenko131ae172007-02-18 13:00:19 +000010298#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010299static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010300change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010301{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010302 uint32_t t;
10303
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010304 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010305 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010306 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010307 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010308 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010309 vrandom.flags &= ~VNOFUNC;
10310 } else {
10311 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010312 t = strtoul(value, NULL, 10);
10313 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010314 }
Eric Andersenef02f822004-03-11 13:34:24 +000010315}
Eric Andersen16767e22004-03-16 05:14:10 +000010316#endif
10317
Denis Vlasenko131ae172007-02-18 13:00:19 +000010318#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010319static int
Eric Andersenc470f442003-07-28 09:56:35 +000010320getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010321{
10322 char *p, *q;
10323 char c = '?';
10324 int done = 0;
10325 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010326 char s[12];
10327 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010328
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010329 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010330 return 1;
10331 optnext = optfirst + *param_optind - 1;
10332
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010333 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010334 p = NULL;
10335 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010336 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010337 if (p == NULL || *p == '\0') {
10338 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010339 p = *optnext;
10340 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010341 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010342 p = NULL;
10343 done = 1;
10344 goto out;
10345 }
10346 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010347 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010348 goto atend;
10349 }
10350
10351 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010352 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010353 if (*q == '\0') {
10354 if (optstr[0] == ':') {
10355 s[0] = c;
10356 s[1] = '\0';
10357 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010358 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010359 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010360 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010361 }
10362 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010363 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010364 }
10365 if (*++q == ':')
10366 q++;
10367 }
10368
10369 if (*++q == ':') {
10370 if (*p == '\0' && (p = *optnext) == NULL) {
10371 if (optstr[0] == ':') {
10372 s[0] = c;
10373 s[1] = '\0';
10374 err |= setvarsafe("OPTARG", s, 0);
10375 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010376 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010377 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010378 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010379 c = '?';
10380 }
Eric Andersenc470f442003-07-28 09:56:35 +000010381 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010382 }
10383
10384 if (p == *optnext)
10385 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010386 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010387 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010388 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010389 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010390 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010391 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010392 *param_optind = optnext - optfirst + 1;
10393 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010394 err |= setvarsafe("OPTIND", s, VNOFUNC);
10395 s[0] = c;
10396 s[1] = '\0';
10397 err |= setvarsafe(optvar, s, 0);
10398 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010399 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010400 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010401 flush_stdout_stderr();
10402 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010403 }
10404 return done;
10405}
Eric Andersenc470f442003-07-28 09:56:35 +000010406
10407/*
10408 * The getopts builtin. Shellparam.optnext points to the next argument
10409 * to be processed. Shellparam.optptr points to the next character to
10410 * be processed in the current argument. If shellparam.optnext is NULL,
10411 * then it's the first time getopts has been called.
10412 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010413static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010414getoptscmd(int argc, char **argv)
10415{
10416 char **optbase;
10417
10418 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010419 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010420 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010421 optbase = shellparam.p;
10422 if (shellparam.optind > shellparam.nparam + 1) {
10423 shellparam.optind = 1;
10424 shellparam.optoff = -1;
10425 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010426 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010427 optbase = &argv[3];
10428 if (shellparam.optind > argc - 2) {
10429 shellparam.optind = 1;
10430 shellparam.optoff = -1;
10431 }
10432 }
10433
10434 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010435 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010436}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010437#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010438
Eric Andersencb57d552001-06-28 07:25:16 +000010439
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010440/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010441
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010442struct heredoc {
10443 struct heredoc *next; /* next here document in list */
10444 union node *here; /* redirection node */
10445 char *eofmark; /* string indicating end of input */
10446 smallint striptabs; /* if set, strip leading tabs */
10447};
10448
10449static smallint tokpushback; /* last token pushed back */
10450static smallint parsebackquote; /* nonzero if we are inside backquotes */
10451static smallint quoteflag; /* set if (part of) last token was quoted */
10452static token_id_t lasttoken; /* last token read (integer id Txxx) */
10453static struct heredoc *heredoclist; /* list of here documents to read */
10454static char *wordtext; /* text of last word returned by readtoken */
10455static struct nodelist *backquotelist;
10456static union node *redirnode;
10457static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010458
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010459static const char *
10460tokname(char *buf, int tok)
10461{
10462 if (tok < TSEMI)
10463 return tokname_array[tok] + 1;
10464 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10465 return buf;
10466}
10467
10468/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010469 * Called when an unexpected token is read during the parse. The argument
10470 * is the token that is expected, or -1 if more than one type of token can
10471 * occur at this point.
10472 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010473static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010474static void
10475raise_error_unexpected_syntax(int token)
10476{
10477 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010478 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010479 int l;
10480
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010481 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010482 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010483 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010484 raise_error_syntax(msg);
10485 /* NOTREACHED */
10486}
Eric Andersencb57d552001-06-28 07:25:16 +000010487
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010488#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010489
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010490/* parsing is heavily cross-recursive, need these forward decls */
10491static union node *andor(void);
10492static union node *pipeline(void);
10493static union node *parse_command(void);
10494static void parseheredoc(void);
10495static char peektoken(void);
10496static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010497
Eric Andersenc470f442003-07-28 09:56:35 +000010498static union node *
10499list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010500{
10501 union node *n1, *n2, *n3;
10502 int tok;
10503
Eric Andersenc470f442003-07-28 09:56:35 +000010504 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10505 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010506 return NULL;
10507 n1 = NULL;
10508 for (;;) {
10509 n2 = andor();
10510 tok = readtoken();
10511 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010512 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010513 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010514 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010515 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010516 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010517 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010518 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010519 n2 = n3;
10520 }
10521 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010522 }
10523 }
10524 if (n1 == NULL) {
10525 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010526 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010527 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010528 n3->type = NSEMI;
10529 n3->nbinary.ch1 = n1;
10530 n3->nbinary.ch2 = n2;
10531 n1 = n3;
10532 }
10533 switch (tok) {
10534 case TBACKGND:
10535 case TSEMI:
10536 tok = readtoken();
10537 /* fall through */
10538 case TNL:
10539 if (tok == TNL) {
10540 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010541 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010542 return n1;
10543 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010544 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010545 }
Eric Andersenc470f442003-07-28 09:56:35 +000010546 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010547 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010548 return n1;
10549 break;
10550 case TEOF:
10551 if (heredoclist)
10552 parseheredoc();
10553 else
Eric Andersenc470f442003-07-28 09:56:35 +000010554 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010555 return n1;
10556 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010557 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010558 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010559 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010560 return n1;
10561 }
10562 }
10563}
10564
Eric Andersenc470f442003-07-28 09:56:35 +000010565static union node *
10566andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010567{
Eric Andersencb57d552001-06-28 07:25:16 +000010568 union node *n1, *n2, *n3;
10569 int t;
10570
Eric Andersencb57d552001-06-28 07:25:16 +000010571 n1 = pipeline();
10572 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010573 t = readtoken();
10574 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010575 t = NAND;
10576 } else if (t == TOR) {
10577 t = NOR;
10578 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010579 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010580 return n1;
10581 }
Eric Andersenc470f442003-07-28 09:56:35 +000010582 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010583 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010584 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010585 n3->type = t;
10586 n3->nbinary.ch1 = n1;
10587 n3->nbinary.ch2 = n2;
10588 n1 = n3;
10589 }
10590}
10591
Eric Andersenc470f442003-07-28 09:56:35 +000010592static union node *
10593pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010594{
Eric Andersencb57d552001-06-28 07:25:16 +000010595 union node *n1, *n2, *pipenode;
10596 struct nodelist *lp, *prev;
10597 int negate;
10598
10599 negate = 0;
10600 TRACE(("pipeline: entered\n"));
10601 if (readtoken() == TNOT) {
10602 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010603 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010604 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010605 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010606 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010607 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010608 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010609 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010610 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010611 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010612 pipenode->npipe.cmdlist = lp;
10613 lp->n = n1;
10614 do {
10615 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010616 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010617 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010618 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010619 prev->next = lp;
10620 } while (readtoken() == TPIPE);
10621 lp->next = NULL;
10622 n1 = pipenode;
10623 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010624 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010625 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010626 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010627 n2->type = NNOT;
10628 n2->nnot.com = n1;
10629 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010630 }
10631 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010632}
10633
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010634static union node *
10635makename(void)
10636{
10637 union node *n;
10638
Denis Vlasenko597906c2008-02-20 16:38:54 +000010639 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010640 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010641 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010642 n->narg.text = wordtext;
10643 n->narg.backquote = backquotelist;
10644 return n;
10645}
10646
10647static void
10648fixredir(union node *n, const char *text, int err)
10649{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010650 int fd;
10651
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010652 TRACE(("Fix redir %s %d\n", text, err));
10653 if (!err)
10654 n->ndup.vname = NULL;
10655
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010656 fd = bb_strtou(text, NULL, 10);
10657 if (!errno && fd >= 0)
10658 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010659 else if (LONE_DASH(text))
10660 n->ndup.dupfd = -1;
10661 else {
10662 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010663 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010664 n->ndup.vname = makename();
10665 }
10666}
10667
10668/*
10669 * Returns true if the text contains nothing to expand (no dollar signs
10670 * or backquotes).
10671 */
10672static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010673noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010674{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010675 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010676
Denys Vlasenkocd716832009-11-28 22:14:02 +010010677 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010678 if (c == CTLQUOTEMARK)
10679 continue;
10680 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010681 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010682 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010683 return 0;
10684 }
10685 return 1;
10686}
10687
10688static void
10689parsefname(void)
10690{
10691 union node *n = redirnode;
10692
10693 if (readtoken() != TWORD)
10694 raise_error_unexpected_syntax(-1);
10695 if (n->type == NHERE) {
10696 struct heredoc *here = heredoc;
10697 struct heredoc *p;
10698 int i;
10699
10700 if (quoteflag == 0)
10701 n->type = NXHERE;
10702 TRACE(("Here document %d\n", n->type));
10703 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010704 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010705 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010706 here->eofmark = wordtext;
10707 here->next = NULL;
10708 if (heredoclist == NULL)
10709 heredoclist = here;
10710 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010711 for (p = heredoclist; p->next; p = p->next)
10712 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010713 p->next = here;
10714 }
10715 } else if (n->type == NTOFD || n->type == NFROMFD) {
10716 fixredir(n, wordtext, 0);
10717 } else {
10718 n->nfile.fname = makename();
10719 }
10720}
Eric Andersencb57d552001-06-28 07:25:16 +000010721
Eric Andersenc470f442003-07-28 09:56:35 +000010722static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010723simplecmd(void)
10724{
10725 union node *args, **app;
10726 union node *n = NULL;
10727 union node *vars, **vpp;
10728 union node **rpp, *redir;
10729 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010730#if ENABLE_ASH_BASH_COMPAT
10731 smallint double_brackets_flag = 0;
10732#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010733
10734 args = NULL;
10735 app = &args;
10736 vars = NULL;
10737 vpp = &vars;
10738 redir = NULL;
10739 rpp = &redir;
10740
10741 savecheckkwd = CHKALIAS;
10742 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010743 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010744 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010745 t = readtoken();
10746 switch (t) {
10747#if ENABLE_ASH_BASH_COMPAT
10748 case TAND: /* "&&" */
10749 case TOR: /* "||" */
10750 if (!double_brackets_flag) {
10751 tokpushback = 1;
10752 goto out;
10753 }
10754 wordtext = (char *) (t == TAND ? "-a" : "-o");
10755#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010756 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010757 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010758 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010759 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010760 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010761#if ENABLE_ASH_BASH_COMPAT
10762 if (strcmp("[[", wordtext) == 0)
10763 double_brackets_flag = 1;
10764 else if (strcmp("]]", wordtext) == 0)
10765 double_brackets_flag = 0;
10766#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010767 n->narg.backquote = backquotelist;
10768 if (savecheckkwd && isassignment(wordtext)) {
10769 *vpp = n;
10770 vpp = &n->narg.next;
10771 } else {
10772 *app = n;
10773 app = &n->narg.next;
10774 savecheckkwd = 0;
10775 }
10776 break;
10777 case TREDIR:
10778 *rpp = n = redirnode;
10779 rpp = &n->nfile.next;
10780 parsefname(); /* read name of redirection file */
10781 break;
10782 case TLP:
10783 if (args && app == &args->narg.next
10784 && !vars && !redir
10785 ) {
10786 struct builtincmd *bcmd;
10787 const char *name;
10788
10789 /* We have a function */
10790 if (readtoken() != TRP)
10791 raise_error_unexpected_syntax(TRP);
10792 name = n->narg.text;
10793 if (!goodname(name)
10794 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10795 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010796 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010797 }
10798 n->type = NDEFUN;
10799 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10800 n->narg.next = parse_command();
10801 return n;
10802 }
10803 /* fall through */
10804 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010805 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010806 goto out;
10807 }
10808 }
10809 out:
10810 *app = NULL;
10811 *vpp = NULL;
10812 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010813 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010814 n->type = NCMD;
10815 n->ncmd.args = args;
10816 n->ncmd.assign = vars;
10817 n->ncmd.redirect = redir;
10818 return n;
10819}
10820
10821static union node *
10822parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010823{
Eric Andersencb57d552001-06-28 07:25:16 +000010824 union node *n1, *n2;
10825 union node *ap, **app;
10826 union node *cp, **cpp;
10827 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010828 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010829 int t;
10830
10831 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010832 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010833
Eric Andersencb57d552001-06-28 07:25:16 +000010834 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010835 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010836 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010837 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010838 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010839 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010840 n1->type = NIF;
10841 n1->nif.test = list(0);
10842 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010843 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010844 n1->nif.ifpart = list(0);
10845 n2 = n1;
10846 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010847 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010848 n2 = n2->nif.elsepart;
10849 n2->type = NIF;
10850 n2->nif.test = list(0);
10851 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010852 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010853 n2->nif.ifpart = list(0);
10854 }
10855 if (lasttoken == TELSE)
10856 n2->nif.elsepart = list(0);
10857 else {
10858 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010859 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010860 }
Eric Andersenc470f442003-07-28 09:56:35 +000010861 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010862 break;
10863 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010864 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010865 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010866 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010867 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010868 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010869 got = readtoken();
10870 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010871 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010872 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010873 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010874 }
10875 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010876 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010877 break;
10878 }
10879 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010880 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010881 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010882 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010883 n1->type = NFOR;
10884 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010885 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010886 if (readtoken() == TIN) {
10887 app = &ap;
10888 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010889 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010890 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010891 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010892 n2->narg.text = wordtext;
10893 n2->narg.backquote = backquotelist;
10894 *app = n2;
10895 app = &n2->narg.next;
10896 }
10897 *app = NULL;
10898 n1->nfor.args = ap;
10899 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010900 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010901 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010902 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010903 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010904 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010905 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010906 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010907 n1->nfor.args = n2;
10908 /*
10909 * Newline or semicolon here is optional (but note
10910 * that the original Bourne shell only allowed NL).
10911 */
10912 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010913 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010914 }
Eric Andersenc470f442003-07-28 09:56:35 +000010915 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010916 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010917 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010918 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010919 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010920 break;
10921 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010922 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010923 n1->type = NCASE;
10924 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010925 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010926 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010927 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010928 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010929 n2->narg.text = wordtext;
10930 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010931 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010932 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010933 } while (readtoken() == TNL);
10934 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010935 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010936 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010937 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010938 checkkwd = CHKNL | CHKKWD;
10939 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010940 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010941 if (lasttoken == TLP)
10942 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010943 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010944 cp->type = NCLIST;
10945 app = &cp->nclist.pattern;
10946 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010947 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010948 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010949 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010950 ap->narg.text = wordtext;
10951 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010952 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010953 break;
10954 app = &ap->narg.next;
10955 readtoken();
10956 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010957 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010958 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010959 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010960 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010961
Eric Andersenc470f442003-07-28 09:56:35 +000010962 cpp = &cp->nclist.next;
10963
10964 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010965 t = readtoken();
10966 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010967 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010968 raise_error_unexpected_syntax(TENDCASE);
10969 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010970 }
Eric Andersenc470f442003-07-28 09:56:35 +000010971 }
Eric Andersencb57d552001-06-28 07:25:16 +000010972 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010973 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010974 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010975 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010976 n1->type = NSUBSHELL;
10977 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010978 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010979 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010980 break;
10981 case TBEGIN:
10982 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010983 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010984 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010985 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010986 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010987 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010988 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010989 }
10990
Eric Andersenc470f442003-07-28 09:56:35 +000010991 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010992 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010993
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010994 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010995 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010996 checkkwd = CHKKWD | CHKALIAS;
10997 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010998 while (readtoken() == TREDIR) {
10999 *rpp = n2 = redirnode;
11000 rpp = &n2->nfile.next;
11001 parsefname();
11002 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011003 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011004 *rpp = NULL;
11005 if (redir) {
11006 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011007 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011008 n2->type = NREDIR;
11009 n2->nredir.n = n1;
11010 n1 = n2;
11011 }
11012 n1->nredir.redirect = redir;
11013 }
Eric Andersencb57d552001-06-28 07:25:16 +000011014 return n1;
11015}
11016
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011017#if ENABLE_ASH_BASH_COMPAT
11018static int decode_dollar_squote(void)
11019{
11020 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11021 int c, cnt;
11022 char *p;
11023 char buf[4];
11024
11025 c = pgetc();
11026 p = strchr(C_escapes, c);
11027 if (p) {
11028 buf[0] = c;
11029 p = buf;
11030 cnt = 3;
11031 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11032 do {
11033 c = pgetc();
11034 *++p = c;
11035 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11036 pungetc();
11037 } else if (c == 'x') { /* \xHH */
11038 do {
11039 c = pgetc();
11040 *++p = c;
11041 } while (isxdigit(c) && --cnt);
11042 pungetc();
11043 if (cnt == 3) { /* \x but next char is "bad" */
11044 c = 'x';
11045 goto unrecognized;
11046 }
11047 } else { /* simple seq like \\ or \t */
11048 p++;
11049 }
11050 *p = '\0';
11051 p = buf;
11052 c = bb_process_escape_sequence((void*)&p);
11053 } else { /* unrecognized "\z": print both chars unless ' or " */
11054 if (c != '\'' && c != '"') {
11055 unrecognized:
11056 c |= 0x100; /* "please encode \, then me" */
11057 }
11058 }
11059 return c;
11060}
11061#endif
11062
Eric Andersencb57d552001-06-28 07:25:16 +000011063/*
11064 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11065 * is not NULL, read a here document. In the latter case, eofmark is the
11066 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011067 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011068 * is the first character of the input token or document.
11069 *
11070 * Because C does not have internal subroutines, I have simulated them
11071 * using goto's to implement the subroutine linkage. The following macros
11072 * will run code that appears at the end of readtoken1.
11073 */
Eric Andersen2870d962001-07-02 17:27:21 +000011074#define CHECKEND() {goto checkend; checkend_return:;}
11075#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11076#define PARSESUB() {goto parsesub; parsesub_return:;}
11077#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11078#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11079#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011080static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011081readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011082{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011083 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011084 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011085 char *out;
11086 int len;
11087 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011088 struct nodelist *bqlist;
11089 smallint quotef;
11090 smallint dblquote;
11091 smallint oldstyle;
11092 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011093#if ENABLE_ASH_EXPAND_PRMT
11094 smallint pssyntax; /* we are expanding a prompt string */
11095#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011096 int varnest; /* levels of variables expansion */
11097 int arinest; /* levels of arithmetic expansion */
11098 int parenlevel; /* levels of parens in arithmetic */
11099 int dqvarnest; /* levels of variables expansion within double quotes */
11100
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011101 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011102
Eric Andersencb57d552001-06-28 07:25:16 +000011103#if __GNUC__
11104 /* Avoid longjmp clobbering */
11105 (void) &out;
11106 (void) &quotef;
11107 (void) &dblquote;
11108 (void) &varnest;
11109 (void) &arinest;
11110 (void) &parenlevel;
11111 (void) &dqvarnest;
11112 (void) &oldstyle;
11113 (void) &prevsyntax;
11114 (void) &syntax;
11115#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011116 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011117 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011118 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011119 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011120#if ENABLE_ASH_EXPAND_PRMT
11121 pssyntax = (syntax == PSSYNTAX);
11122 if (pssyntax)
11123 syntax = DQSYNTAX;
11124#endif
11125 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011126 varnest = 0;
11127 arinest = 0;
11128 parenlevel = 0;
11129 dqvarnest = 0;
11130
11131 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011132 loop:
11133 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011134 CHECKEND(); /* set c to PEOF if at end of here document */
11135 for (;;) { /* until end of line or end of word */
11136 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11137 switch (SIT(c, syntax)) {
11138 case CNL: /* '\n' */
11139 if (syntax == BASESYNTAX)
11140 goto endword; /* exit outer loop */
11141 USTPUTC(c, out);
11142 g_parsefile->linno++;
11143 setprompt_if(doprompt, 2);
11144 c = pgetc();
11145 goto loop; /* continue outer loop */
11146 case CWORD:
11147 USTPUTC(c, out);
11148 break;
11149 case CCTL:
11150 if (eofmark == NULL || dblquote)
11151 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011152#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011153 if (c == '\\' && bash_dollar_squote) {
11154 c = decode_dollar_squote();
11155 if (c & 0x100) {
11156 USTPUTC('\\', out);
11157 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011158 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011159 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011160#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011161 USTPUTC(c, out);
11162 break;
11163 case CBACK: /* backslash */
11164 c = pgetc_without_PEOA();
11165 if (c == PEOF) {
11166 USTPUTC(CTLESC, out);
11167 USTPUTC('\\', out);
11168 pungetc();
11169 } else if (c == '\n') {
11170 setprompt_if(doprompt, 2);
11171 } else {
11172#if ENABLE_ASH_EXPAND_PRMT
11173 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011174 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011175 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011176 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011177#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011178 /* Backslash is retained if we are in "str" and next char isn't special */
11179 if (dblquote
11180 && c != '\\'
11181 && c != '`'
11182 && c != '$'
11183 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011184 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011185 USTPUTC(CTLESC, out);
11186 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011187 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011188 if (SIT(c, SQSYNTAX) == CCTL)
11189 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011190 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011191 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011192 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011193 break;
11194 case CSQUOTE:
11195 syntax = SQSYNTAX;
11196 quotemark:
11197 if (eofmark == NULL) {
11198 USTPUTC(CTLQUOTEMARK, out);
11199 }
11200 break;
11201 case CDQUOTE:
11202 syntax = DQSYNTAX;
11203 dblquote = 1;
11204 goto quotemark;
11205 case CENDQUOTE:
11206 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11207 if (eofmark != NULL && arinest == 0
11208 && varnest == 0
11209 ) {
11210 USTPUTC(c, out);
11211 } else {
11212 if (dqvarnest == 0) {
11213 syntax = BASESYNTAX;
11214 dblquote = 0;
11215 }
11216 quotef = 1;
11217 goto quotemark;
11218 }
11219 break;
11220 case CVAR: /* '$' */
11221 PARSESUB(); /* parse substitution */
11222 break;
11223 case CENDVAR: /* '}' */
11224 if (varnest > 0) {
11225 varnest--;
11226 if (dqvarnest > 0) {
11227 dqvarnest--;
11228 }
11229 c = CTLENDVAR;
11230 }
11231 USTPUTC(c, out);
11232 break;
11233#if ENABLE_SH_MATH_SUPPORT
11234 case CLP: /* '(' in arithmetic */
11235 parenlevel++;
11236 USTPUTC(c, out);
11237 break;
11238 case CRP: /* ')' in arithmetic */
11239 if (parenlevel > 0) {
11240 parenlevel--;
11241 } else {
11242 if (pgetc() == ')') {
11243 if (--arinest == 0) {
11244 syntax = prevsyntax;
11245 dblquote = (syntax == DQSYNTAX);
11246 c = CTLENDARI;
11247 }
11248 } else {
11249 /*
11250 * unbalanced parens
11251 * (don't 2nd guess - no error)
11252 */
11253 pungetc();
11254 }
11255 }
11256 USTPUTC(c, out);
11257 break;
11258#endif
11259 case CBQUOTE: /* '`' */
11260 PARSEBACKQOLD();
11261 break;
11262 case CENDFILE:
11263 goto endword; /* exit outer loop */
11264 case CIGN:
11265 break;
11266 default:
11267 if (varnest == 0) {
11268#if ENABLE_ASH_BASH_COMPAT
11269 if (c == '&') {
11270 if (pgetc() == '>')
11271 c = 0x100 + '>'; /* flag &> */
11272 pungetc();
11273 }
11274#endif
11275 goto endword; /* exit outer loop */
11276 }
11277 IF_ASH_ALIAS(if (c != PEOA))
11278 USTPUTC(c, out);
11279 }
11280 c = pgetc_fast();
11281 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011282 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011283
Mike Frysinger98c52642009-04-02 10:02:37 +000011284#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011285 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011286 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011287#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011288 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011289 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011290 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011291 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011292 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011293 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011294 }
11295 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011296 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011297 out = stackblock();
11298 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011299 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011300 && quotef == 0
11301 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011302 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011303 PARSEREDIR(); /* passed as params: out, c */
11304 lasttoken = TREDIR;
11305 return lasttoken;
11306 }
11307 /* else: non-number X seen, interpret it
11308 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011309 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011310 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011311 }
11312 quoteflag = quotef;
11313 backquotelist = bqlist;
11314 grabstackblock(len);
11315 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011316 lasttoken = TWORD;
11317 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011318/* end of readtoken routine */
11319
Eric Andersencb57d552001-06-28 07:25:16 +000011320/*
11321 * Check to see whether we are at the end of the here document. When this
11322 * is called, c is set to the first character of the next input line. If
11323 * we are at the end of the here document, this routine sets the c to PEOF.
11324 */
Eric Andersenc470f442003-07-28 09:56:35 +000011325checkend: {
11326 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011327#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011328 if (c == PEOA)
11329 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011330#endif
11331 if (striptabs) {
11332 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011333 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011334 }
Eric Andersenc470f442003-07-28 09:56:35 +000011335 }
11336 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011337 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011338 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011339
Eric Andersenc470f442003-07-28 09:56:35 +000011340 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011341 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11342 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011343 if (*p == '\n' && *q == '\0') {
11344 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011345 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011346 needprompt = doprompt;
11347 } else {
11348 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011349 }
11350 }
11351 }
11352 }
Eric Andersenc470f442003-07-28 09:56:35 +000011353 goto checkend_return;
11354}
Eric Andersencb57d552001-06-28 07:25:16 +000011355
Eric Andersencb57d552001-06-28 07:25:16 +000011356/*
11357 * Parse a redirection operator. The variable "out" points to a string
11358 * specifying the fd to be redirected. The variable "c" contains the
11359 * first character of the redirection operator.
11360 */
Eric Andersenc470f442003-07-28 09:56:35 +000011361parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011362 /* out is already checked to be a valid number or "" */
11363 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011364 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011365
Denis Vlasenko597906c2008-02-20 16:38:54 +000011366 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011367 if (c == '>') {
11368 np->nfile.fd = 1;
11369 c = pgetc();
11370 if (c == '>')
11371 np->type = NAPPEND;
11372 else if (c == '|')
11373 np->type = NCLOBBER;
11374 else if (c == '&')
11375 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011376 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011377 else {
11378 np->type = NTO;
11379 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011380 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011381 }
11382#if ENABLE_ASH_BASH_COMPAT
11383 else if (c == 0x100 + '>') { /* this flags &> redirection */
11384 np->nfile.fd = 1;
11385 pgetc(); /* this is '>', no need to check */
11386 np->type = NTO2;
11387 }
11388#endif
11389 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011390 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011391 c = pgetc();
11392 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011393 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011394 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011395 np = stzalloc(sizeof(struct nhere));
11396 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011397 }
11398 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011399 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011400 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011401 c = pgetc();
11402 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011403 heredoc->striptabs = 1;
11404 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011405 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011406 pungetc();
11407 }
11408 break;
11409
11410 case '&':
11411 np->type = NFROMFD;
11412 break;
11413
11414 case '>':
11415 np->type = NFROMTO;
11416 break;
11417
11418 default:
11419 np->type = NFROM;
11420 pungetc();
11421 break;
11422 }
Eric Andersencb57d552001-06-28 07:25:16 +000011423 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011424 if (fd >= 0)
11425 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011426 redirnode = np;
11427 goto parseredir_return;
11428}
Eric Andersencb57d552001-06-28 07:25:16 +000011429
Eric Andersencb57d552001-06-28 07:25:16 +000011430/*
11431 * Parse a substitution. At this point, we have read the dollar sign
11432 * and nothing else.
11433 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011434
11435/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11436 * (assuming ascii char codes, as the original implementation did) */
11437#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011438 (((unsigned)(c) - 33 < 32) \
11439 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011440parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011441 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011442 int typeloc;
11443 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011444
Eric Andersenc470f442003-07-28 09:56:35 +000011445 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011446 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011447 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011448 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011449#if ENABLE_ASH_BASH_COMPAT
11450 if (c == '\'')
11451 bash_dollar_squote = 1;
11452 else
11453#endif
11454 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011455 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011456 } else if (c == '(') {
11457 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011458 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011459#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011460 PARSEARITH();
11461#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011462 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011463#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011464 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011465 pungetc();
11466 PARSEBACKQNEW();
11467 }
11468 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011469 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011470 USTPUTC(CTLVAR, out);
11471 typeloc = out - (char *)stackblock();
11472 USTPUTC(VSNORMAL, out);
11473 subtype = VSNORMAL;
11474 if (c == '{') {
11475 c = pgetc();
11476 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011477 c = pgetc();
11478 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011479 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011480 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011481 subtype = VSLENGTH; /* ${#VAR} */
11482 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011483 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011484 }
Eric Andersenc470f442003-07-28 09:56:35 +000011485 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011486 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011487 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011488 do {
11489 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011490 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011491 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011492 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011493 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011494 do {
11495 STPUTC(c, out);
11496 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011497 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011498 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011499 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011500 USTPUTC(c, out);
11501 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011502 } else {
11503 badsub:
11504 raise_error_syntax("bad substitution");
11505 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011506 if (c != '}' && subtype == VSLENGTH) {
11507 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011508 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011509 }
Eric Andersencb57d552001-06-28 07:25:16 +000011510
Eric Andersenc470f442003-07-28 09:56:35 +000011511 STPUTC('=', out);
11512 flags = 0;
11513 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011514 /* ${VAR...} but not $VAR or ${#VAR} */
11515 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011516 switch (c) {
11517 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011518 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011519#if ENABLE_ASH_BASH_COMPAT
11520 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011521//TODO: support more general format ${v:EXPR:EXPR},
11522// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011523 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011524 pungetc();
11525 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011526 }
11527#endif
11528 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011529 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011530 default: {
11531 static const char types[] ALIGN1 = "}-+?=";
11532 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011533 if (p == NULL)
11534 goto badsub;
11535 subtype = p - types + VSNORMAL;
11536 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011537 }
Eric Andersenc470f442003-07-28 09:56:35 +000011538 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011539 case '#': {
11540 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011541 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011542 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011543 if (c != cc)
11544 goto do_pungetc;
11545 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011546 break;
11547 }
11548#if ENABLE_ASH_BASH_COMPAT
11549 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011550 /* ${v/[/]pattern/repl} */
11551//TODO: encode pattern and repl separately.
11552// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011553 subtype = VSREPLACE;
11554 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011555 if (c != '/')
11556 goto do_pungetc;
11557 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011558 break;
11559#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011560 }
Eric Andersenc470f442003-07-28 09:56:35 +000011561 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011562 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011563 pungetc();
11564 }
11565 if (dblquote || arinest)
11566 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011567 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011568 if (subtype != VSNORMAL) {
11569 varnest++;
11570 if (dblquote || arinest) {
11571 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011572 }
11573 }
11574 }
Eric Andersenc470f442003-07-28 09:56:35 +000011575 goto parsesub_return;
11576}
Eric Andersencb57d552001-06-28 07:25:16 +000011577
Eric Andersencb57d552001-06-28 07:25:16 +000011578/*
11579 * Called to parse command substitutions. Newstyle is set if the command
11580 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11581 * list of commands (passed by reference), and savelen is the number of
11582 * characters on the top of the stack which must be preserved.
11583 */
Eric Andersenc470f442003-07-28 09:56:35 +000011584parsebackq: {
11585 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011586 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011587 union node *n;
11588 char *volatile str;
11589 struct jmploc jmploc;
11590 struct jmploc *volatile savehandler;
11591 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011592 smallint saveprompt = 0;
11593
Eric Andersencb57d552001-06-28 07:25:16 +000011594#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011595 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011596#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011597 savepbq = parsebackquote;
11598 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011599 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011600 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011601 exception_handler = savehandler;
11602 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011603 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011604 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011605 str = NULL;
11606 savelen = out - (char *)stackblock();
11607 if (savelen > 0) {
11608 str = ckmalloc(savelen);
11609 memcpy(str, stackblock(), savelen);
11610 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011611 savehandler = exception_handler;
11612 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011613 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011614 if (oldstyle) {
11615 /* We must read until the closing backquote, giving special
11616 treatment to some slashes, and then push the string and
11617 reread it as input, interpreting it normally. */
11618 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011619 size_t psavelen;
11620 char *pstr;
11621
Eric Andersenc470f442003-07-28 09:56:35 +000011622 STARTSTACKSTR(pout);
11623 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011624 int pc;
11625
11626 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011627 pc = pgetc();
11628 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011629 case '`':
11630 goto done;
11631
11632 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011633 pc = pgetc();
11634 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011635 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011636 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011637 /*
11638 * If eating a newline, avoid putting
11639 * the newline into the new character
11640 * stream (via the STPUTC after the
11641 * switch).
11642 */
11643 continue;
11644 }
11645 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011646 && (!dblquote || pc != '"')
11647 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011648 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011649 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011650 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011651 break;
11652 }
11653 /* fall through */
11654
11655 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011656 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011657 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011658 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011659
11660 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011661 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011662 needprompt = doprompt;
11663 break;
11664
11665 default:
11666 break;
11667 }
11668 STPUTC(pc, pout);
11669 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011670 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011671 STPUTC('\0', pout);
11672 psavelen = pout - (char *)stackblock();
11673 if (psavelen > 0) {
11674 pstr = grabstackstr(pout);
11675 setinputstring(pstr);
11676 }
11677 }
11678 nlpp = &bqlist;
11679 while (*nlpp)
11680 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011681 *nlpp = stzalloc(sizeof(**nlpp));
11682 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011683 parsebackquote = oldstyle;
11684
11685 if (oldstyle) {
11686 saveprompt = doprompt;
11687 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011688 }
11689
Eric Andersenc470f442003-07-28 09:56:35 +000011690 n = list(2);
11691
11692 if (oldstyle)
11693 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011694 else if (readtoken() != TRP)
11695 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011696
11697 (*nlpp)->n = n;
11698 if (oldstyle) {
11699 /*
11700 * Start reading from old file again, ignoring any pushed back
11701 * tokens left from the backquote parsing
11702 */
11703 popfile();
11704 tokpushback = 0;
11705 }
11706 while (stackblocksize() <= savelen)
11707 growstackblock();
11708 STARTSTACKSTR(out);
11709 if (str) {
11710 memcpy(out, str, savelen);
11711 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011712 INT_OFF;
11713 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011714 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011715 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011716 }
11717 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011718 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011719 if (arinest || dblquote)
11720 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11721 else
11722 USTPUTC(CTLBACKQ, out);
11723 if (oldstyle)
11724 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011725 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011726}
11727
Mike Frysinger98c52642009-04-02 10:02:37 +000011728#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011729/*
11730 * Parse an arithmetic expansion (indicate start of one and set state)
11731 */
Eric Andersenc470f442003-07-28 09:56:35 +000011732parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011733 if (++arinest == 1) {
11734 prevsyntax = syntax;
11735 syntax = ARISYNTAX;
11736 USTPUTC(CTLARI, out);
11737 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011738 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011739 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011740 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011741 } else {
11742 /*
11743 * we collapse embedded arithmetic expansion to
11744 * parenthesis, which should be equivalent
11745 */
11746 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011747 }
Eric Andersenc470f442003-07-28 09:56:35 +000011748 goto parsearith_return;
11749}
11750#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011751
Eric Andersenc470f442003-07-28 09:56:35 +000011752} /* end of readtoken */
11753
Eric Andersencb57d552001-06-28 07:25:16 +000011754/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011755 * Read the next input token.
11756 * If the token is a word, we set backquotelist to the list of cmds in
11757 * backquotes. We set quoteflag to true if any part of the word was
11758 * quoted.
11759 * If the token is TREDIR, then we set redirnode to a structure containing
11760 * the redirection.
11761 * In all cases, the variable startlinno is set to the number of the line
11762 * on which the token starts.
11763 *
11764 * [Change comment: here documents and internal procedures]
11765 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11766 * word parsing code into a separate routine. In this case, readtoken
11767 * doesn't need to have any internal procedures, but parseword does.
11768 * We could also make parseoperator in essence the main routine, and
11769 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011770 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011771#define NEW_xxreadtoken
11772#ifdef NEW_xxreadtoken
11773/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011774static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011775 '\n', '(', ')', /* singles */
11776 '&', '|', ';', /* doubles */
11777 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011778};
Eric Andersencb57d552001-06-28 07:25:16 +000011779
Denis Vlasenko834dee72008-10-07 09:18:30 +000011780#define xxreadtoken_singles 3
11781#define xxreadtoken_doubles 3
11782
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011783static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011784 TNL, TLP, TRP, /* only single occurrence allowed */
11785 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11786 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011787 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011788};
11789
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011790static int
11791xxreadtoken(void)
11792{
11793 int c;
11794
11795 if (tokpushback) {
11796 tokpushback = 0;
11797 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011798 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011799 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011800 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011801 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011802 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011803 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011804 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011805
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011806 if (c == '#') {
11807 while ((c = pgetc()) != '\n' && c != PEOF)
11808 continue;
11809 pungetc();
11810 } else if (c == '\\') {
11811 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011812 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011813 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011814 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011815 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011816 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011817 } else {
11818 const char *p;
11819
11820 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11821 if (c != PEOF) {
11822 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011823 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011824 needprompt = doprompt;
11825 }
11826
11827 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011828 if (p == NULL)
11829 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011830
Denis Vlasenko834dee72008-10-07 09:18:30 +000011831 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11832 int cc = pgetc();
11833 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011834 p += xxreadtoken_doubles + 1;
11835 } else {
11836 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011837#if ENABLE_ASH_BASH_COMPAT
11838 if (c == '&' && cc == '>') /* &> */
11839 break; /* return readtoken1(...) */
11840#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011841 }
11842 }
11843 }
11844 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11845 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011846 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011847 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011848
11849 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011850}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011851#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011852#define RETURN(token) return lasttoken = token
11853static int
11854xxreadtoken(void)
11855{
11856 int c;
11857
11858 if (tokpushback) {
11859 tokpushback = 0;
11860 return lasttoken;
11861 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011862 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011863 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011864 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011865 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011866 switch (c) {
11867 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011868 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011869 continue;
11870 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011871 while ((c = pgetc()) != '\n' && c != PEOF)
11872 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011873 pungetc();
11874 continue;
11875 case '\\':
11876 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011877 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011878 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011879 continue;
11880 }
11881 pungetc();
11882 goto breakloop;
11883 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011884 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011885 needprompt = doprompt;
11886 RETURN(TNL);
11887 case PEOF:
11888 RETURN(TEOF);
11889 case '&':
11890 if (pgetc() == '&')
11891 RETURN(TAND);
11892 pungetc();
11893 RETURN(TBACKGND);
11894 case '|':
11895 if (pgetc() == '|')
11896 RETURN(TOR);
11897 pungetc();
11898 RETURN(TPIPE);
11899 case ';':
11900 if (pgetc() == ';')
11901 RETURN(TENDCASE);
11902 pungetc();
11903 RETURN(TSEMI);
11904 case '(':
11905 RETURN(TLP);
11906 case ')':
11907 RETURN(TRP);
11908 default:
11909 goto breakloop;
11910 }
11911 }
11912 breakloop:
11913 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11914#undef RETURN
11915}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011916#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011917
11918static int
11919readtoken(void)
11920{
11921 int t;
11922#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011923 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011924#endif
11925
11926#if ENABLE_ASH_ALIAS
11927 top:
11928#endif
11929
11930 t = xxreadtoken();
11931
11932 /*
11933 * eat newlines
11934 */
11935 if (checkkwd & CHKNL) {
11936 while (t == TNL) {
11937 parseheredoc();
11938 t = xxreadtoken();
11939 }
11940 }
11941
11942 if (t != TWORD || quoteflag) {
11943 goto out;
11944 }
11945
11946 /*
11947 * check for keywords
11948 */
11949 if (checkkwd & CHKKWD) {
11950 const char *const *pp;
11951
11952 pp = findkwd(wordtext);
11953 if (pp) {
11954 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011955 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011956 goto out;
11957 }
11958 }
11959
11960 if (checkkwd & CHKALIAS) {
11961#if ENABLE_ASH_ALIAS
11962 struct alias *ap;
11963 ap = lookupalias(wordtext, 1);
11964 if (ap != NULL) {
11965 if (*ap->val) {
11966 pushstring(ap->val, ap);
11967 }
11968 goto top;
11969 }
11970#endif
11971 }
11972 out:
11973 checkkwd = 0;
11974#if DEBUG
11975 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011976 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011977 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011978 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011979#endif
11980 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011981}
11982
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011983static char
11984peektoken(void)
11985{
11986 int t;
11987
11988 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011989 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011990 return tokname_array[t][0];
11991}
Eric Andersencb57d552001-06-28 07:25:16 +000011992
11993/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011994 * Read and parse a command. Returns NODE_EOF on end of file.
11995 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011996 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011997static union node *
11998parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011999{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012000 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000012001
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012002 tokpushback = 0;
12003 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012004 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012005 needprompt = 0;
12006 t = readtoken();
12007 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012008 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012009 if (t == TNL)
12010 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012011 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012012 return list(1);
12013}
12014
12015/*
12016 * Input any here documents.
12017 */
12018static void
12019parseheredoc(void)
12020{
12021 struct heredoc *here;
12022 union node *n;
12023
12024 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012025 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012026
12027 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012028 setprompt_if(needprompt, 2);
12029 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012030 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012031 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012032 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012033 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012034 n->narg.text = wordtext;
12035 n->narg.backquote = backquotelist;
12036 here->here->nhere.doc = n;
12037 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012038 }
Eric Andersencb57d552001-06-28 07:25:16 +000012039}
12040
12041
12042/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012043 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012044 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012045#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012046static const char *
12047expandstr(const char *ps)
12048{
12049 union node n;
12050
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012051 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12052 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012053 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012054 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012055 popfile();
12056
12057 n.narg.type = NARG;
12058 n.narg.next = NULL;
12059 n.narg.text = wordtext;
12060 n.narg.backquote = backquotelist;
12061
12062 expandarg(&n, NULL, 0);
12063 return stackblock();
12064}
12065#endif
12066
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012067/*
12068 * Execute a command or commands contained in a string.
12069 */
12070static int
12071evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012072{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012073 union node *n;
12074 struct stackmark smark;
12075 int skip;
12076
12077 setinputstring(s);
12078 setstackmark(&smark);
12079
12080 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012081 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012082 evaltree(n, 0);
12083 popstackmark(&smark);
12084 skip = evalskip;
12085 if (skip)
12086 break;
12087 }
12088 popfile();
12089
12090 skip &= mask;
12091 evalskip = skip;
12092 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012093}
12094
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012095/*
12096 * The eval command.
12097 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012098static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012099evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012100{
12101 char *p;
12102 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012103
Denis Vlasenko68404f12008-03-17 09:00:54 +000012104 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012105 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012106 argv += 2;
12107 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012108 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012109 for (;;) {
12110 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012111 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012112 if (p == NULL)
12113 break;
12114 STPUTC(' ', concat);
12115 }
12116 STPUTC('\0', concat);
12117 p = grabstackstr(concat);
12118 }
12119 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012120 }
12121 return exitstatus;
12122}
12123
12124/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012125 * Read and execute commands.
12126 * "Top" is nonzero for the top level command loop;
12127 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012128 */
12129static int
12130cmdloop(int top)
12131{
12132 union node *n;
12133 struct stackmark smark;
12134 int inter;
12135 int numeof = 0;
12136
12137 TRACE(("cmdloop(%d) called\n", top));
12138 for (;;) {
12139 int skip;
12140
12141 setstackmark(&smark);
12142#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012143 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012144 showjobs(stderr, SHOW_CHANGED);
12145#endif
12146 inter = 0;
12147 if (iflag && top) {
12148 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012149 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012150 }
12151 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012152#if DEBUG
12153 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012154 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012155#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012156 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012157 if (!top || numeof >= 50)
12158 break;
12159 if (!stoppedjobs()) {
12160 if (!Iflag)
12161 break;
12162 out2str("\nUse \"exit\" to leave shell.\n");
12163 }
12164 numeof++;
12165 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012166 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12167 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012168 numeof = 0;
12169 evaltree(n, 0);
12170 }
12171 popstackmark(&smark);
12172 skip = evalskip;
12173
12174 if (skip) {
12175 evalskip = 0;
12176 return skip & SKIPEVAL;
12177 }
12178 }
12179 return 0;
12180}
12181
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012182/*
12183 * Take commands from a file. To be compatible we should do a path
12184 * search for the file, which is necessary to find sub-commands.
12185 */
12186static char *
12187find_dot_file(char *name)
12188{
12189 char *fullname;
12190 const char *path = pathval();
12191 struct stat statb;
12192
12193 /* don't try this for absolute or relative paths */
12194 if (strchr(name, '/'))
12195 return name;
12196
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012197 /* IIRC standards do not say whether . is to be searched.
12198 * And it is even smaller this way, making it unconditional for now:
12199 */
12200 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12201 fullname = name;
12202 goto try_cur_dir;
12203 }
12204
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012205 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012206 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012207 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12208 /*
12209 * Don't bother freeing here, since it will
12210 * be freed by the caller.
12211 */
12212 return fullname;
12213 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012214 if (fullname != name)
12215 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012216 }
12217
12218 /* not found in the PATH */
12219 ash_msg_and_raise_error("%s: not found", name);
12220 /* NOTREACHED */
12221}
12222
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012223static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012224dotcmd(int argc, char **argv)
12225{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012226 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012227 struct strlist *sp;
12228 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012229
12230 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012231 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012232
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012233 if (!argv[1]) {
12234 /* bash says: "bash: .: filename argument required" */
12235 return 2; /* bash compat */
12236 }
12237
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012238 /* "false; . empty_file; echo $?" should print 0, not 1: */
12239 exitstatus = 0;
12240
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012241 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012242
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012243 argv += 2;
12244 argc -= 2;
12245 if (argc) { /* argc > 0, argv[0] != NULL */
12246 saveparam = shellparam;
12247 shellparam.malloced = 0;
12248 shellparam.nparam = argc;
12249 shellparam.p = argv;
12250 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012251
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012252 setinputfile(fullname, INPUT_PUSH_FILE);
12253 commandname = fullname;
12254 cmdloop(0);
12255 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012256
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012257 if (argc) {
12258 freeparam(&shellparam);
12259 shellparam = saveparam;
12260 };
12261
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012262 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012263}
12264
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012265static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012266exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012267{
12268 if (stoppedjobs())
12269 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012270 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012271 exitstatus = number(argv[1]);
12272 raise_exception(EXEXIT);
12273 /* NOTREACHED */
12274}
12275
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012276/*
12277 * Read a file containing shell functions.
12278 */
12279static void
12280readcmdfile(char *name)
12281{
12282 setinputfile(name, INPUT_PUSH_FILE);
12283 cmdloop(0);
12284 popfile();
12285}
12286
12287
Denis Vlasenkocc571512007-02-23 21:10:35 +000012288/* ============ find_command inplementation */
12289
12290/*
12291 * Resolve a command name. If you change this routine, you may have to
12292 * change the shellexec routine as well.
12293 */
12294static void
12295find_command(char *name, struct cmdentry *entry, int act, const char *path)
12296{
12297 struct tblentry *cmdp;
12298 int idx;
12299 int prev;
12300 char *fullname;
12301 struct stat statb;
12302 int e;
12303 int updatetbl;
12304 struct builtincmd *bcmd;
12305
12306 /* If name contains a slash, don't use PATH or hash table */
12307 if (strchr(name, '/') != NULL) {
12308 entry->u.index = -1;
12309 if (act & DO_ABS) {
12310 while (stat(name, &statb) < 0) {
12311#ifdef SYSV
12312 if (errno == EINTR)
12313 continue;
12314#endif
12315 entry->cmdtype = CMDUNKNOWN;
12316 return;
12317 }
12318 }
12319 entry->cmdtype = CMDNORMAL;
12320 return;
12321 }
12322
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012323/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012324
12325 updatetbl = (path == pathval());
12326 if (!updatetbl) {
12327 act |= DO_ALTPATH;
12328 if (strstr(path, "%builtin") != NULL)
12329 act |= DO_ALTBLTIN;
12330 }
12331
12332 /* If name is in the table, check answer will be ok */
12333 cmdp = cmdlookup(name, 0);
12334 if (cmdp != NULL) {
12335 int bit;
12336
12337 switch (cmdp->cmdtype) {
12338 default:
12339#if DEBUG
12340 abort();
12341#endif
12342 case CMDNORMAL:
12343 bit = DO_ALTPATH;
12344 break;
12345 case CMDFUNCTION:
12346 bit = DO_NOFUNC;
12347 break;
12348 case CMDBUILTIN:
12349 bit = DO_ALTBLTIN;
12350 break;
12351 }
12352 if (act & bit) {
12353 updatetbl = 0;
12354 cmdp = NULL;
12355 } else if (cmdp->rehash == 0)
12356 /* if not invalidated by cd, we're done */
12357 goto success;
12358 }
12359
12360 /* If %builtin not in path, check for builtin next */
12361 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012362 if (bcmd) {
12363 if (IS_BUILTIN_REGULAR(bcmd))
12364 goto builtin_success;
12365 if (act & DO_ALTPATH) {
12366 if (!(act & DO_ALTBLTIN))
12367 goto builtin_success;
12368 } else if (builtinloc <= 0) {
12369 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012370 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012371 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012372
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012373#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012374 {
12375 int applet_no = find_applet_by_name(name);
12376 if (applet_no >= 0) {
12377 entry->cmdtype = CMDNORMAL;
12378 entry->u.index = -2 - applet_no;
12379 return;
12380 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012381 }
12382#endif
12383
Denis Vlasenkocc571512007-02-23 21:10:35 +000012384 /* We have to search path. */
12385 prev = -1; /* where to start */
12386 if (cmdp && cmdp->rehash) { /* doing a rehash */
12387 if (cmdp->cmdtype == CMDBUILTIN)
12388 prev = builtinloc;
12389 else
12390 prev = cmdp->param.index;
12391 }
12392
12393 e = ENOENT;
12394 idx = -1;
12395 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012396 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012397 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012398 /* NB: code below will still use fullname
12399 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012400 idx++;
12401 if (pathopt) {
12402 if (prefix(pathopt, "builtin")) {
12403 if (bcmd)
12404 goto builtin_success;
12405 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012406 }
12407 if ((act & DO_NOFUNC)
12408 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012409 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012410 continue;
12411 }
12412 }
12413 /* if rehash, don't redo absolute path names */
12414 if (fullname[0] == '/' && idx <= prev) {
12415 if (idx < prev)
12416 continue;
12417 TRACE(("searchexec \"%s\": no change\n", name));
12418 goto success;
12419 }
12420 while (stat(fullname, &statb) < 0) {
12421#ifdef SYSV
12422 if (errno == EINTR)
12423 continue;
12424#endif
12425 if (errno != ENOENT && errno != ENOTDIR)
12426 e = errno;
12427 goto loop;
12428 }
12429 e = EACCES; /* if we fail, this will be the error */
12430 if (!S_ISREG(statb.st_mode))
12431 continue;
12432 if (pathopt) { /* this is a %func directory */
12433 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012434 /* NB: stalloc will return space pointed by fullname
12435 * (because we don't have any intervening allocations
12436 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012437 readcmdfile(fullname);
12438 cmdp = cmdlookup(name, 0);
12439 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12440 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12441 stunalloc(fullname);
12442 goto success;
12443 }
12444 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12445 if (!updatetbl) {
12446 entry->cmdtype = CMDNORMAL;
12447 entry->u.index = idx;
12448 return;
12449 }
12450 INT_OFF;
12451 cmdp = cmdlookup(name, 1);
12452 cmdp->cmdtype = CMDNORMAL;
12453 cmdp->param.index = idx;
12454 INT_ON;
12455 goto success;
12456 }
12457
12458 /* We failed. If there was an entry for this command, delete it */
12459 if (cmdp && updatetbl)
12460 delete_cmd_entry();
12461 if (act & DO_ERR)
12462 ash_msg("%s: %s", name, errmsg(e, "not found"));
12463 entry->cmdtype = CMDUNKNOWN;
12464 return;
12465
12466 builtin_success:
12467 if (!updatetbl) {
12468 entry->cmdtype = CMDBUILTIN;
12469 entry->u.cmd = bcmd;
12470 return;
12471 }
12472 INT_OFF;
12473 cmdp = cmdlookup(name, 1);
12474 cmdp->cmdtype = CMDBUILTIN;
12475 cmdp->param.cmd = bcmd;
12476 INT_ON;
12477 success:
12478 cmdp->rehash = 0;
12479 entry->cmdtype = cmdp->cmdtype;
12480 entry->u = cmdp->param;
12481}
12482
12483
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012484/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012485
Eric Andersencb57d552001-06-28 07:25:16 +000012486/*
Eric Andersencb57d552001-06-28 07:25:16 +000012487 * The trap builtin.
12488 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012489static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012490trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012491{
12492 char *action;
12493 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012494 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012495
Eric Andersenc470f442003-07-28 09:56:35 +000012496 nextopt(nullstr);
12497 ap = argptr;
12498 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012499 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012500 char *tr = trap_ptr[signo];
12501 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012502 /* note: bash adds "SIG", but only if invoked
12503 * as "bash". If called as "sh", or if set -o posix,
12504 * then it prints short signal names.
12505 * We are printing short names: */
12506 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012507 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012508 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012509 /* trap_ptr != trap only if we are in special-cased `trap` code.
12510 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012511 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012512 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012513 }
12514 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012515 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012516 if (trap_ptr != trap) {
12517 free(trap_ptr);
12518 trap_ptr = trap;
12519 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012520 */
Eric Andersencb57d552001-06-28 07:25:16 +000012521 return 0;
12522 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012523
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012524 action = NULL;
12525 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012526 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012527 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012528 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012529 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012530 if (signo < 0) {
12531 /* Mimic bash message exactly */
12532 ash_msg("%s: invalid signal specification", *ap);
12533 exitcode = 1;
12534 goto next;
12535 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012536 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012537 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012538 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012539 action = NULL;
12540 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012541 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012542 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012543 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012544 if (action)
12545 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012546 trap[signo] = action;
12547 if (signo != 0)
12548 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012549 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012550 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012551 ap++;
12552 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012553 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012554}
12555
Eric Andersenc470f442003-07-28 09:56:35 +000012556
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012557/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012558
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012559#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012560/*
12561 * Lists available builtins
12562 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012563static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012564helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012565{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012566 unsigned col;
12567 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012568
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012569 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012570 "Built-in commands:\n"
12571 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012572 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012573 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012574 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012575 if (col > 60) {
12576 out1fmt("\n");
12577 col = 0;
12578 }
12579 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012580#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012581 {
12582 const char *a = applet_names;
12583 while (*a) {
12584 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12585 if (col > 60) {
12586 out1fmt("\n");
12587 col = 0;
12588 }
12589 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012590 }
12591 }
12592#endif
12593 out1fmt("\n\n");
12594 return EXIT_SUCCESS;
12595}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012596#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012597
Eric Andersencb57d552001-06-28 07:25:16 +000012598/*
Eric Andersencb57d552001-06-28 07:25:16 +000012599 * The export and readonly commands.
12600 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012601static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012602exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012603{
12604 struct var *vp;
12605 char *name;
12606 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012607 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012608 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012609
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012610 if (nextopt("p") != 'p') {
12611 aptr = argptr;
12612 name = *aptr;
12613 if (name) {
12614 do {
12615 p = strchr(name, '=');
12616 if (p != NULL) {
12617 p++;
12618 } else {
12619 vp = *findvar(hashvar(name), name);
12620 if (vp) {
12621 vp->flags |= flag;
12622 continue;
12623 }
Eric Andersencb57d552001-06-28 07:25:16 +000012624 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012625 setvar(name, p, flag);
12626 } while ((name = *++aptr) != NULL);
12627 return 0;
12628 }
Eric Andersencb57d552001-06-28 07:25:16 +000012629 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012630 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012631 return 0;
12632}
12633
Eric Andersencb57d552001-06-28 07:25:16 +000012634/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012635 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012636 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012637static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012638unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012639{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012640 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012641
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012642 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012643 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012644 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012645}
12646
Eric Andersencb57d552001-06-28 07:25:16 +000012647/*
Eric Andersencb57d552001-06-28 07:25:16 +000012648 * The unset builtin command. We unset the function before we unset the
12649 * variable to allow a function to be unset when there is a readonly variable
12650 * with the same name.
12651 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012652static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012653unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012654{
12655 char **ap;
12656 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012657 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012658 int ret = 0;
12659
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012660 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012661 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012662 }
Eric Andersencb57d552001-06-28 07:25:16 +000012663
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012664 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012665 if (flag != 'f') {
12666 i = unsetvar(*ap);
12667 ret |= i;
12668 if (!(i & 2))
12669 continue;
12670 }
12671 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012672 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012673 }
Eric Andersenc470f442003-07-28 09:56:35 +000012674 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012675}
12676
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012677static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012678 ' ', offsetof(struct tms, tms_utime),
12679 '\n', offsetof(struct tms, tms_stime),
12680 ' ', offsetof(struct tms, tms_cutime),
12681 '\n', offsetof(struct tms, tms_cstime),
12682 0
12683};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012684static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012685timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012686{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012687 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012688 const unsigned char *p;
12689 struct tms buf;
12690
12691 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012692 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012693
12694 p = timescmd_str;
12695 do {
12696 t = *(clock_t *)(((char *) &buf) + p[1]);
12697 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012698 t = t % clk_tck;
12699 out1fmt("%lum%lu.%03lus%c",
12700 s / 60, s % 60,
12701 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012702 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012703 p += 2;
12704 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012705
Eric Andersencb57d552001-06-28 07:25:16 +000012706 return 0;
12707}
12708
Mike Frysinger98c52642009-04-02 10:02:37 +000012709#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012710/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012711 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012712 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012713 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012714 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012715 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012716static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012717letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012718{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012719 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012720
Denis Vlasenko68404f12008-03-17 09:00:54 +000012721 argv++;
12722 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012723 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012724 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012725 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012726 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012727
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012728 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012729}
Eric Andersenc470f442003-07-28 09:56:35 +000012730#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012731
Eric Andersenc470f442003-07-28 09:56:35 +000012732/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012733 * The read builtin. Options:
12734 * -r Do not interpret '\' specially
12735 * -s Turn off echo (tty only)
12736 * -n NCHARS Read NCHARS max
12737 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12738 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12739 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012740 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012741 * TODO: bash also has:
12742 * -a ARRAY Read into array[0],[1],etc
12743 * -d DELIM End on DELIM char, not newline
12744 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012745 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012746static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012747readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012748{
Denys Vlasenko73067272010-01-12 22:11:24 +010012749 char *opt_n = NULL;
12750 char *opt_p = NULL;
12751 char *opt_t = NULL;
12752 char *opt_u = NULL;
12753 int read_flags = 0;
12754 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012755 int i;
12756
Denys Vlasenko73067272010-01-12 22:11:24 +010012757 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012758 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012759 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012760 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012761 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012762 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012763 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012764 break;
12765 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012766 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012767 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012768 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012769 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012770 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012771 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012772 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012773 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012774 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012775 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012776 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012777 default:
12778 break;
12779 }
Eric Andersenc470f442003-07-28 09:56:35 +000012780 }
Paul Fox02eb9342005-09-07 16:56:02 +000012781
Denys Vlasenko03dad222010-01-12 23:29:57 +010012782 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012783 argptr,
12784 bltinlookup("IFS"), /* can be NULL */
12785 read_flags,
12786 opt_n,
12787 opt_p,
12788 opt_t,
12789 opt_u
12790 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012791
Denys Vlasenko73067272010-01-12 22:11:24 +010012792 if ((uintptr_t)r > 1)
12793 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012794
Denys Vlasenko73067272010-01-12 22:11:24 +010012795 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012796}
12797
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012798static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012799umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012800{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012801 static const char permuser[3] ALIGN1 = "ugo";
12802 static const char permmode[3] ALIGN1 = "rwx";
12803 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012804 S_IRUSR, S_IWUSR, S_IXUSR,
12805 S_IRGRP, S_IWGRP, S_IXGRP,
12806 S_IROTH, S_IWOTH, S_IXOTH
12807 };
12808
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012809 /* TODO: use bb_parse_mode() instead */
12810
Eric Andersenc470f442003-07-28 09:56:35 +000012811 char *ap;
12812 mode_t mask;
12813 int i;
12814 int symbolic_mode = 0;
12815
12816 while (nextopt("S") != '\0') {
12817 symbolic_mode = 1;
12818 }
12819
Denis Vlasenkob012b102007-02-19 22:43:01 +000012820 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012821 mask = umask(0);
12822 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012823 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012824
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012825 ap = *argptr;
12826 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012827 if (symbolic_mode) {
12828 char buf[18];
12829 char *p = buf;
12830
12831 for (i = 0; i < 3; i++) {
12832 int j;
12833
12834 *p++ = permuser[i];
12835 *p++ = '=';
12836 for (j = 0; j < 3; j++) {
12837 if ((mask & permmask[3 * i + j]) == 0) {
12838 *p++ = permmode[j];
12839 }
12840 }
12841 *p++ = ',';
12842 }
12843 *--p = 0;
12844 puts(buf);
12845 } else {
12846 out1fmt("%.4o\n", mask);
12847 }
12848 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012849 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012850 mask = 0;
12851 do {
12852 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012853 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012854 mask = (mask << 3) + (*ap - '0');
12855 } while (*++ap != '\0');
12856 umask(mask);
12857 } else {
12858 mask = ~mask & 0777;
12859 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012860 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012861 }
12862 umask(~mask & 0777);
12863 }
12864 }
12865 return 0;
12866}
12867
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012868static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012869ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012870{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012871 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012872}
12873
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012874/* ============ main() and helpers */
12875
12876/*
12877 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012878 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012879static void
12880exitshell(void)
12881{
12882 struct jmploc loc;
12883 char *p;
12884 int status;
12885
12886 status = exitstatus;
12887 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12888 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012889 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012890/* dash bug: it just does _exit(exitstatus) here
12891 * but we have to do setjobctl(0) first!
12892 * (bug is still not fixed in dash-0.5.3 - if you run dash
12893 * under Midnight Commander, on exit from dash MC is backgrounded) */
12894 status = exitstatus;
12895 goto out;
12896 }
12897 exception_handler = &loc;
12898 p = trap[0];
12899 if (p) {
12900 trap[0] = NULL;
12901 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012902 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012903 }
12904 flush_stdout_stderr();
12905 out:
12906 setjobctl(0);
12907 _exit(status);
12908 /* NOTREACHED */
12909}
12910
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012911static void
12912init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012913{
12914 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012915 /* we will never free this */
12916 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012917
12918 /* from trap.c: */
12919 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012920 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12921 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12922 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012923 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012924
12925 /* from var.c: */
12926 {
12927 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012928 const char *p;
12929 struct stat st1, st2;
12930
12931 initvar();
12932 for (envp = environ; envp && *envp; envp++) {
12933 if (strchr(*envp, '=')) {
12934 setvareq(*envp, VEXPORT|VTEXTFIXED);
12935 }
12936 }
12937
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012938 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012939
12940 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012941 if (p) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012942 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012943 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
12944 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012945 p = '\0';
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012946 }
12947 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012948 setpwd(p, 0);
12949 }
12950}
12951
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012952
12953//usage:#define ash_trivial_usage
Denys Vlasenko6b6af532011-03-08 10:24:17 +010012954//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012955//usage:#define ash_full_usage "\n\n"
12956//usage: "Unix shell interpreter"
12957
12958//usage:#if ENABLE_FEATURE_SH_IS_ASH
12959//usage:# define sh_trivial_usage ash_trivial_usage
12960//usage:# define sh_full_usage ash_full_usage
12961//usage:#endif
12962//usage:#if ENABLE_FEATURE_BASH_IS_ASH
12963//usage:# define bash_trivial_usage ash_trivial_usage
12964//usage:# define bash_full_usage ash_full_usage
12965//usage:#endif
12966
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012967/*
12968 * Process the shell command line arguments.
12969 */
12970static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012971procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012972{
12973 int i;
12974 const char *xminusc;
12975 char **xargv;
12976
12977 xargv = argv;
12978 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012979 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012980 xargv++;
12981 for (i = 0; i < NOPTS; i++)
12982 optlist[i] = 2;
12983 argptr = xargv;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012984 if (options(/*cmdline:*/ 1)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012985 /* it already printed err message */
12986 raise_exception(EXERROR);
12987 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012988 xargv = argptr;
12989 xminusc = minusc;
12990 if (*xargv == NULL) {
12991 if (xminusc)
12992 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12993 sflag = 1;
12994 }
12995 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12996 iflag = 1;
12997 if (mflag == 2)
12998 mflag = iflag;
12999 for (i = 0; i < NOPTS; i++)
13000 if (optlist[i] == 2)
13001 optlist[i] = 0;
13002#if DEBUG == 2
13003 debug = 1;
13004#endif
13005 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13006 if (xminusc) {
13007 minusc = *xargv++;
13008 if (*xargv)
13009 goto setarg0;
13010 } else if (!sflag) {
13011 setinputfile(*xargv, 0);
13012 setarg0:
13013 arg0 = *xargv++;
13014 commandname = arg0;
13015 }
13016
13017 shellparam.p = xargv;
13018#if ENABLE_ASH_GETOPTS
13019 shellparam.optind = 1;
13020 shellparam.optoff = -1;
13021#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013022 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013023 while (*xargv) {
13024 shellparam.nparam++;
13025 xargv++;
13026 }
13027 optschanged();
13028}
13029
13030/*
13031 * Read /etc/profile or .profile.
13032 */
13033static void
13034read_profile(const char *name)
13035{
13036 int skip;
13037
13038 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13039 return;
13040 skip = cmdloop(0);
13041 popfile();
13042 if (skip)
13043 exitshell();
13044}
13045
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013046/*
13047 * This routine is called when an error or an interrupt occurs in an
13048 * interactive shell and control is returned to the main command loop.
13049 */
13050static void
13051reset(void)
13052{
13053 /* from eval.c: */
13054 evalskip = 0;
13055 loopnest = 0;
13056 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013057 g_parsefile->left_in_buffer = 0;
13058 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013059 popallfiles();
13060 /* from parser.c: */
13061 tokpushback = 0;
13062 checkkwd = 0;
13063 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013064 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013065}
13066
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013067#if PROFILE
13068static short profile_buf[16384];
13069extern int etext();
13070#endif
13071
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013072/*
13073 * Main routine. We initialize things, parse the arguments, execute
13074 * profiles if we're a login shell, and then call cmdloop to execute
13075 * commands. The setjmp call sets up the location to jump to when an
13076 * exception occurs. When an exception occurs the variable "state"
13077 * is used to figure out how far we had gotten.
13078 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013079int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013080int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013081{
Mike Frysinger98c52642009-04-02 10:02:37 +000013082 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013083 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013084 struct jmploc jmploc;
13085 struct stackmark smark;
13086
Denis Vlasenko01631112007-12-16 17:20:38 +000013087 /* Initialize global data */
13088 INIT_G_misc();
13089 INIT_G_memstack();
13090 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013091#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013092 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013093#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013094 INIT_G_cmdtable();
13095
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013096#if PROFILE
13097 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13098#endif
13099
13100#if ENABLE_FEATURE_EDITING
13101 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13102#endif
13103 state = 0;
13104 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013105 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013106 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013107
13108 reset();
13109
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013110 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013111 if (e == EXERROR)
13112 exitstatus = 2;
13113 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013114 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013115 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013116 }
13117 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013118 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013119 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013120
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013121 popstackmark(&smark);
13122 FORCE_INT_ON; /* enable interrupts */
13123 if (s == 1)
13124 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013125 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013126 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013127 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013128 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013129 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013130 }
13131 exception_handler = &jmploc;
13132#if DEBUG
13133 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013134 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013135 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013136#endif
13137 rootpid = getpid();
13138
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013139 init();
13140 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013141 procargs(argv);
13142
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013143#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13144 if (iflag) {
13145 const char *hp = lookupvar("HISTFILE");
13146
13147 if (hp == NULL) {
13148 hp = lookupvar("HOME");
13149 if (hp != NULL) {
13150 char *defhp = concat_path_file(hp, ".ash_history");
13151 setvar("HISTFILE", defhp, 0);
13152 free(defhp);
13153 }
13154 }
13155 }
13156#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013157 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013158 isloginsh = 1;
13159 if (isloginsh) {
13160 state = 1;
13161 read_profile("/etc/profile");
13162 state1:
13163 state = 2;
13164 read_profile(".profile");
13165 }
13166 state2:
13167 state = 3;
13168 if (
13169#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013170 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013171#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013172 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013173 ) {
13174 shinit = lookupvar("ENV");
13175 if (shinit != NULL && *shinit != '\0') {
13176 read_profile(shinit);
13177 }
13178 }
13179 state3:
13180 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013181 if (minusc) {
13182 /* evalstring pushes parsefile stack.
13183 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013184 * is one of stacked source fds.
13185 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013186 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013187 // ^^ not necessary since now we special-case fd 0
13188 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013189 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013190 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013191
13192 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013193#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013194 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013195 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013196 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013197 line_input_state->hist_file = hp;
13198 }
13199#endif
13200 state4: /* XXX ??? - why isn't this before the "if" statement */
13201 cmdloop(1);
13202 }
13203#if PROFILE
13204 monitor(0);
13205#endif
13206#ifdef GPROF
13207 {
13208 extern void _mcleanup(void);
13209 _mcleanup();
13210 }
13211#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013212 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013213 exitshell();
13214 /* NOTREACHED */
13215}
13216
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013217
Eric Andersendf82f612001-06-28 07:46:40 +000013218/*-
13219 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013220 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013221 *
13222 * This code is derived from software contributed to Berkeley by
13223 * Kenneth Almquist.
13224 *
13225 * Redistribution and use in source and binary forms, with or without
13226 * modification, are permitted provided that the following conditions
13227 * are met:
13228 * 1. Redistributions of source code must retain the above copyright
13229 * notice, this list of conditions and the following disclaimer.
13230 * 2. Redistributions in binary form must reproduce the above copyright
13231 * notice, this list of conditions and the following disclaimer in the
13232 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013233 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013234 * may be used to endorse or promote products derived from this software
13235 * without specific prior written permission.
13236 *
13237 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13238 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13239 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13240 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13241 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13242 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13243 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13244 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13245 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13246 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13247 * SUCH DAMAGE.
13248 */