blob: d197fa19a4d44cdad139b2f2224e617e72e2e567 [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 Vlasenkoa8915072007-02-23 21:10:06 +00003494 break;
3495 case S_IGN:
3496 act.sa_handler = SIG_IGN;
3497 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003498 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003499
3500 /* flags and mask matter only if !DFL and !IGN, but we do it
3501 * for all cases for more deterministic behavior:
3502 */
3503 act.sa_flags = 0;
3504 sigfillset(&act.sa_mask);
3505
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003506 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003507
3508 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003509}
3510
3511/* mode flags for set_curjob */
3512#define CUR_DELETE 2
3513#define CUR_RUNNING 1
3514#define CUR_STOPPED 0
3515
3516/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003517#define DOWAIT_NONBLOCK WNOHANG
3518#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003519
3520#if JOBS
3521/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003522static int initialpgrp; //references:2
3523static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003524#endif
3525/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003526static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003527/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003528static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003529/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003530static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003531/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003532static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003533
3534static void
3535set_curjob(struct job *jp, unsigned mode)
3536{
3537 struct job *jp1;
3538 struct job **jpp, **curp;
3539
3540 /* first remove from list */
3541 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003542 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003543 jp1 = *jpp;
3544 if (jp1 == jp)
3545 break;
3546 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003547 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003548 *jpp = jp1->prev_job;
3549
3550 /* Then re-insert in correct position */
3551 jpp = curp;
3552 switch (mode) {
3553 default:
3554#if DEBUG
3555 abort();
3556#endif
3557 case CUR_DELETE:
3558 /* job being deleted */
3559 break;
3560 case CUR_RUNNING:
3561 /* newly created job or backgrounded job,
3562 put after all stopped jobs. */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003563 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003564 jp1 = *jpp;
3565#if JOBS
3566 if (!jp1 || jp1->state != JOBSTOPPED)
3567#endif
3568 break;
3569 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003570 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003571 /* FALLTHROUGH */
3572#if JOBS
3573 case CUR_STOPPED:
3574#endif
3575 /* newly stopped job - becomes curjob */
3576 jp->prev_job = *jpp;
3577 *jpp = jp;
3578 break;
3579 }
3580}
3581
3582#if JOBS || DEBUG
3583static int
3584jobno(const struct job *jp)
3585{
3586 return jp - jobtab + 1;
3587}
3588#endif
3589
3590/*
3591 * Convert a job name to a job structure.
3592 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003593#if !JOBS
3594#define getjob(name, getctl) getjob(name)
3595#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003596static struct job *
3597getjob(const char *name, int getctl)
3598{
3599 struct job *jp;
3600 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003601 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003602 unsigned num;
3603 int c;
3604 const char *p;
3605 char *(*match)(const char *, const char *);
3606
3607 jp = curjob;
3608 p = name;
3609 if (!p)
3610 goto currentjob;
3611
3612 if (*p != '%')
3613 goto err;
3614
3615 c = *++p;
3616 if (!c)
3617 goto currentjob;
3618
3619 if (!p[1]) {
3620 if (c == '+' || c == '%') {
3621 currentjob:
3622 err_msg = "No current job";
3623 goto check;
3624 }
3625 if (c == '-') {
3626 if (jp)
3627 jp = jp->prev_job;
3628 err_msg = "No previous job";
3629 check:
3630 if (!jp)
3631 goto err;
3632 goto gotit;
3633 }
3634 }
3635
3636 if (is_number(p)) {
3637 num = atoi(p);
3638 if (num < njobs) {
3639 jp = jobtab + num - 1;
3640 if (jp->used)
3641 goto gotit;
3642 goto err;
3643 }
3644 }
3645
3646 match = prefix;
3647 if (*p == '?') {
3648 match = strstr;
3649 p++;
3650 }
3651
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003652 found = NULL;
3653 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003654 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003655 if (found)
3656 goto err;
3657 found = jp;
3658 err_msg = "%s: ambiguous";
3659 }
3660 jp = jp->prev_job;
3661 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003662 if (!found)
3663 goto err;
3664 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003665
3666 gotit:
3667#if JOBS
3668 err_msg = "job %s not created under job control";
3669 if (getctl && jp->jobctl == 0)
3670 goto err;
3671#endif
3672 return jp;
3673 err:
3674 ash_msg_and_raise_error(err_msg, name);
3675}
3676
3677/*
3678 * Mark a job structure as unused.
3679 */
3680static void
3681freejob(struct job *jp)
3682{
3683 struct procstat *ps;
3684 int i;
3685
3686 INT_OFF;
3687 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003688 if (ps->ps_cmd != nullstr)
3689 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003690 }
3691 if (jp->ps != &jp->ps0)
3692 free(jp->ps);
3693 jp->used = 0;
3694 set_curjob(jp, CUR_DELETE);
3695 INT_ON;
3696}
3697
3698#if JOBS
3699static void
3700xtcsetpgrp(int fd, pid_t pgrp)
3701{
3702 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003703 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003704}
3705
3706/*
3707 * Turn job control on and off.
3708 *
3709 * Note: This code assumes that the third arg to ioctl is a character
3710 * pointer, which is true on Berkeley systems but not System V. Since
3711 * System V doesn't have job control yet, this isn't a problem now.
3712 *
3713 * Called with interrupts off.
3714 */
3715static void
3716setjobctl(int on)
3717{
3718 int fd;
3719 int pgrp;
3720
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003721 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003722 return;
3723 if (on) {
3724 int ofd;
3725 ofd = fd = open(_PATH_TTY, O_RDWR);
3726 if (fd < 0) {
3727 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3728 * That sometimes helps to acquire controlling tty.
3729 * Obviously, a workaround for bugs when someone
3730 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003731 fd = 2;
3732 while (!isatty(fd))
3733 if (--fd < 0)
3734 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003735 }
3736 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003737 if (ofd >= 0)
3738 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003739 if (fd < 0)
3740 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003741 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003742 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003743 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003744 pgrp = tcgetpgrp(fd);
3745 if (pgrp < 0) {
3746 out:
3747 ash_msg("can't access tty; job control turned off");
3748 mflag = on = 0;
3749 goto close;
3750 }
3751 if (pgrp == getpgrp())
3752 break;
3753 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003754 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003755 initialpgrp = pgrp;
3756
3757 setsignal(SIGTSTP);
3758 setsignal(SIGTTOU);
3759 setsignal(SIGTTIN);
3760 pgrp = rootpid;
3761 setpgid(0, pgrp);
3762 xtcsetpgrp(fd, pgrp);
3763 } else {
3764 /* turning job control off */
3765 fd = ttyfd;
3766 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003767 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003768 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003769 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770 setpgid(0, pgrp);
3771 setsignal(SIGTSTP);
3772 setsignal(SIGTTOU);
3773 setsignal(SIGTTIN);
3774 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003775 if (fd >= 0)
3776 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003777 fd = -1;
3778 }
3779 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003780 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003781}
3782
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003783static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784killcmd(int argc, char **argv)
3785{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003786 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003787 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003788 do {
3789 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003790 /*
3791 * "kill %N" - job kill
3792 * Converting to pgrp / pid kill
3793 */
3794 struct job *jp;
3795 char *dst;
3796 int j, n;
3797
3798 jp = getjob(argv[i], 0);
3799 /*
3800 * In jobs started under job control, we signal
3801 * entire process group by kill -PGRP_ID.
3802 * This happens, f.e., in interactive shell.
3803 *
3804 * Otherwise, we signal each child via
3805 * kill PID1 PID2 PID3.
3806 * Testcases:
3807 * sh -c 'sleep 1|sleep 1 & kill %1'
3808 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3809 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3810 */
3811 n = jp->nprocs; /* can't be 0 (I hope) */
3812 if (jp->jobctl)
3813 n = 1;
3814 dst = alloca(n * sizeof(int)*4);
3815 argv[i] = dst;
3816 for (j = 0; j < n; j++) {
3817 struct procstat *ps = &jp->ps[j];
3818 /* Skip non-running and not-stopped members
3819 * (i.e. dead members) of the job
3820 */
3821 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3822 continue;
3823 /*
3824 * kill_main has matching code to expect
3825 * leading space. Needed to not confuse
3826 * negative pids with "kill -SIGNAL_NO" syntax
3827 */
3828 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3829 }
3830 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003831 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003832 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003833 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003834 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003835}
3836
3837static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003838showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003840 struct procstat *ps;
3841 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842
Denys Vlasenko285ad152009-12-04 23:02:27 +01003843 psend = jp->ps + jp->nprocs;
3844 for (ps = jp->ps + 1; ps < psend; ps++)
3845 printf(" | %s", ps->ps_cmd);
3846 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003847 flush_stdout_stderr();
3848}
3849
3850
3851static int
3852restartjob(struct job *jp, int mode)
3853{
3854 struct procstat *ps;
3855 int i;
3856 int status;
3857 pid_t pgid;
3858
3859 INT_OFF;
3860 if (jp->state == JOBDONE)
3861 goto out;
3862 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003863 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003864 if (mode == FORK_FG)
3865 xtcsetpgrp(ttyfd, pgid);
3866 killpg(pgid, SIGCONT);
3867 ps = jp->ps;
3868 i = jp->nprocs;
3869 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003870 if (WIFSTOPPED(ps->ps_status)) {
3871 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003872 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003873 ps++;
3874 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003875 out:
3876 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3877 INT_ON;
3878 return status;
3879}
3880
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003881static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003882fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883{
3884 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003885 int mode;
3886 int retval;
3887
3888 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3889 nextopt(nullstr);
3890 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003891 do {
3892 jp = getjob(*argv, 1);
3893 if (mode == FORK_BG) {
3894 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003895 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003896 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003897 out1str(jp->ps[0].ps_cmd);
3898 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003899 retval = restartjob(jp, mode);
3900 } while (*argv && *++argv);
3901 return retval;
3902}
3903#endif
3904
3905static int
3906sprint_status(char *s, int status, int sigonly)
3907{
3908 int col;
3909 int st;
3910
3911 col = 0;
3912 if (!WIFEXITED(status)) {
3913#if JOBS
3914 if (WIFSTOPPED(status))
3915 st = WSTOPSIG(status);
3916 else
3917#endif
3918 st = WTERMSIG(status);
3919 if (sigonly) {
3920 if (st == SIGINT || st == SIGPIPE)
3921 goto out;
3922#if JOBS
3923 if (WIFSTOPPED(status))
3924 goto out;
3925#endif
3926 }
3927 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003928//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003929 col = fmtstr(s, 32, strsignal(st));
3930 if (WCOREDUMP(status)) {
3931 col += fmtstr(s + col, 16, " (core dumped)");
3932 }
3933 } else if (!sigonly) {
3934 st = WEXITSTATUS(status);
3935 if (st)
3936 col = fmtstr(s, 16, "Done(%d)", st);
3937 else
3938 col = fmtstr(s, 16, "Done");
3939 }
3940 out:
3941 return col;
3942}
3943
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003945dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003946{
3947 int pid;
3948 int status;
3949 struct job *jp;
3950 struct job *thisjob;
3951 int state;
3952
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003953 TRACE(("dowait(0x%x) called\n", wait_flags));
3954
3955 /* Do a wait system call. If job control is compiled in, we accept
3956 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3957 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003958 if (doing_jobctl)
3959 wait_flags |= WUNTRACED;
3960 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003961 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3962 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003963 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003965
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966 INT_OFF;
3967 thisjob = NULL;
3968 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003969 struct procstat *ps;
3970 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971 if (jp->state == JOBDONE)
3972 continue;
3973 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003974 ps = jp->ps;
3975 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003976 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003977 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003978 TRACE(("Job %d: changing status of proc %d "
3979 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003980 jobno(jp), pid, ps->ps_status, status));
3981 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003982 thisjob = jp;
3983 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003984 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003985 state = JOBRUNNING;
3986#if JOBS
3987 if (state == JOBRUNNING)
3988 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003989 if (WIFSTOPPED(ps->ps_status)) {
3990 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003991 state = JOBSTOPPED;
3992 }
3993#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003994 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003995 if (thisjob)
3996 goto gotjob;
3997 }
3998#if JOBS
3999 if (!WIFSTOPPED(status))
4000#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004001 jobless--;
4002 goto out;
4003
4004 gotjob:
4005 if (state != JOBRUNNING) {
4006 thisjob->changed = 1;
4007
4008 if (thisjob->state != state) {
4009 TRACE(("Job %d: changing state from %d to %d\n",
4010 jobno(thisjob), thisjob->state, state));
4011 thisjob->state = state;
4012#if JOBS
4013 if (state == JOBSTOPPED) {
4014 set_curjob(thisjob, CUR_STOPPED);
4015 }
4016#endif
4017 }
4018 }
4019
4020 out:
4021 INT_ON;
4022
4023 if (thisjob && thisjob == job) {
4024 char s[48 + 1];
4025 int len;
4026
4027 len = sprint_status(s, status, 1);
4028 if (len) {
4029 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004030 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004031 out2str(s);
4032 }
4033 }
4034 return pid;
4035}
4036
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004037static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004038blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004039{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004040 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004041 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004042 raise_exception(EXSIG);
4043 return pid;
4044}
4045
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004046#if JOBS
4047static void
4048showjob(FILE *out, struct job *jp, int mode)
4049{
4050 struct procstat *ps;
4051 struct procstat *psend;
4052 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004053 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054 char s[80];
4055
4056 ps = jp->ps;
4057
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004058 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004060 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004061 return;
4062 }
4063
4064 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004065 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004066
4067 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004068 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004069 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004070 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004071
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004072 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004073 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004074
4075 psend = ps + jp->nprocs;
4076
4077 if (jp->state == JOBRUNNING) {
4078 strcpy(s + col, "Running");
4079 col += sizeof("Running") - 1;
4080 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004081 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082 if (jp->state == JOBSTOPPED)
4083 status = jp->stopstatus;
4084 col += sprint_status(s + col, status, 0);
4085 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004086 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004087
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004088 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4089 * or prints several "PID | <cmdN>" lines,
4090 * depending on SHOW_PIDS bit.
4091 * We do not print status of individual processes
4092 * between PID and <cmdN>. bash does it, but not very well:
4093 * first line shows overall job status, not process status,
4094 * making it impossible to know 1st process status.
4095 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004097 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004098 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004099 s[0] = '\0';
4100 col = 33;
4101 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004102 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004103 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004104 fprintf(out, "%s%*c%s%s",
4105 s,
4106 33 - col >= 0 ? 33 - col : 0, ' ',
4107 ps == jp->ps ? "" : "| ",
4108 ps->ps_cmd
4109 );
4110 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004111 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004112
4113 jp->changed = 0;
4114
4115 if (jp->state == JOBDONE) {
4116 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4117 freejob(jp);
4118 }
4119}
4120
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004121/*
4122 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4123 * statuses have changed since the last call to showjobs.
4124 */
4125static void
4126showjobs(FILE *out, int mode)
4127{
4128 struct job *jp;
4129
Denys Vlasenko883cea42009-07-11 15:31:59 +02004130 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004131
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004132 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004133 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134 continue;
4135
4136 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004137 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004138 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004139 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004140 }
4141}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004142
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004143static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004144jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004145{
4146 int mode, m;
4147
4148 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004149 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004150 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004151 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004152 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004153 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004154 }
4155
4156 argv = argptr;
4157 if (*argv) {
4158 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004159 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004160 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004161 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004162 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004163 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004164
4165 return 0;
4166}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004167#endif /* JOBS */
4168
Michael Abbott359da5e2009-12-04 23:03:29 +01004169/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004170static int
4171getstatus(struct job *job)
4172{
4173 int status;
4174 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004175 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176
Michael Abbott359da5e2009-12-04 23:03:29 +01004177 /* Fetch last member's status */
4178 ps = job->ps + job->nprocs - 1;
4179 status = ps->ps_status;
4180 if (pipefail) {
4181 /* "set -o pipefail" mode: use last _nonzero_ status */
4182 while (status == 0 && --ps >= job->ps)
4183 status = ps->ps_status;
4184 }
4185
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004186 retval = WEXITSTATUS(status);
4187 if (!WIFEXITED(status)) {
4188#if JOBS
4189 retval = WSTOPSIG(status);
4190 if (!WIFSTOPPED(status))
4191#endif
4192 {
4193 /* XXX: limits number of signals */
4194 retval = WTERMSIG(status);
4195#if JOBS
4196 if (retval == SIGINT)
4197 job->sigint = 1;
4198#endif
4199 }
4200 retval += 128;
4201 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004202 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004203 jobno(job), job->nprocs, status, retval));
4204 return retval;
4205}
4206
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004207static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004208waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004209{
4210 struct job *job;
4211 int retval;
4212 struct job *jp;
4213
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004214 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004215 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004216
4217 nextopt(nullstr);
4218 retval = 0;
4219
4220 argv = argptr;
4221 if (!*argv) {
4222 /* wait for all jobs */
4223 for (;;) {
4224 jp = curjob;
4225 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004226 if (!jp) /* no running procs */
4227 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004228 if (jp->state == JOBRUNNING)
4229 break;
4230 jp->waited = 1;
4231 jp = jp->prev_job;
4232 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004233 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004234 /* man bash:
4235 * "When bash is waiting for an asynchronous command via
4236 * the wait builtin, the reception of a signal for which a trap
4237 * has been set will cause the wait builtin to return immediately
4238 * with an exit status greater than 128, immediately after which
4239 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004240 *
4241 * blocking_wait_with_raise_on_sig raises signal handlers
4242 * if it gets no pid (pid < 0). However,
4243 * if child sends us a signal *and immediately exits*,
4244 * blocking_wait_with_raise_on_sig gets pid > 0
4245 * and does not handle pending_sig. Check this case: */
4246 if (pending_sig)
4247 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004248 }
4249 }
4250
4251 retval = 127;
4252 do {
4253 if (**argv != '%') {
4254 pid_t pid = number(*argv);
4255 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004256 while (1) {
4257 if (!job)
4258 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004259 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004260 break;
4261 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004262 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004263 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004265 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004266 /* loop until process terminated or stopped */
4267 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004268 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004269 job->waited = 1;
4270 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004271 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004272 } while (*++argv);
4273
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004274 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004275 return retval;
4276}
4277
4278static struct job *
4279growjobtab(void)
4280{
4281 size_t len;
4282 ptrdiff_t offset;
4283 struct job *jp, *jq;
4284
4285 len = njobs * sizeof(*jp);
4286 jq = jobtab;
4287 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4288
4289 offset = (char *)jp - (char *)jq;
4290 if (offset) {
4291 /* Relocate pointers */
4292 size_t l = len;
4293
4294 jq = (struct job *)((char *)jq + l);
4295 while (l) {
4296 l -= sizeof(*jp);
4297 jq--;
4298#define joff(p) ((struct job *)((char *)(p) + l))
4299#define jmove(p) (p) = (void *)((char *)(p) + offset)
4300 if (joff(jp)->ps == &jq->ps0)
4301 jmove(joff(jp)->ps);
4302 if (joff(jp)->prev_job)
4303 jmove(joff(jp)->prev_job);
4304 }
4305 if (curjob)
4306 jmove(curjob);
4307#undef joff
4308#undef jmove
4309 }
4310
4311 njobs += 4;
4312 jobtab = jp;
4313 jp = (struct job *)((char *)jp + len);
4314 jq = jp + 3;
4315 do {
4316 jq->used = 0;
4317 } while (--jq >= jp);
4318 return jp;
4319}
4320
4321/*
4322 * Return a new job structure.
4323 * Called with interrupts off.
4324 */
4325static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004326makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004327{
4328 int i;
4329 struct job *jp;
4330
4331 for (i = njobs, jp = jobtab; ; jp++) {
4332 if (--i < 0) {
4333 jp = growjobtab();
4334 break;
4335 }
4336 if (jp->used == 0)
4337 break;
4338 if (jp->state != JOBDONE || !jp->waited)
4339 continue;
4340#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004341 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004342 continue;
4343#endif
4344 freejob(jp);
4345 break;
4346 }
4347 memset(jp, 0, sizeof(*jp));
4348#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004349 /* jp->jobctl is a bitfield.
4350 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004351 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004352 jp->jobctl = 1;
4353#endif
4354 jp->prev_job = curjob;
4355 curjob = jp;
4356 jp->used = 1;
4357 jp->ps = &jp->ps0;
4358 if (nprocs > 1) {
4359 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4360 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004361 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004362 jobno(jp)));
4363 return jp;
4364}
4365
4366#if JOBS
4367/*
4368 * Return a string identifying a command (to be printed by the
4369 * jobs command).
4370 */
4371static char *cmdnextc;
4372
4373static void
4374cmdputs(const char *s)
4375{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004376 static const char vstype[VSTYPE + 1][3] = {
4377 "", "}", "-", "+", "?", "=",
4378 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004379 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004380 };
4381
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004382 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004383 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004384 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004385 unsigned char c;
4386 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004388
Denys Vlasenko46a14772009-12-10 21:27:13 +01004389 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004390 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4391 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004392 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004393 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004394 switch (c) {
4395 case CTLESC:
4396 c = *p++;
4397 break;
4398 case CTLVAR:
4399 subtype = *p++;
4400 if ((subtype & VSTYPE) == VSLENGTH)
4401 str = "${#";
4402 else
4403 str = "${";
4404 if (!(subtype & VSQUOTE) == !(quoted & 1))
4405 goto dostr;
4406 quoted ^= 1;
4407 c = '"';
4408 break;
4409 case CTLENDVAR:
4410 str = "\"}" + !(quoted & 1);
4411 quoted >>= 1;
4412 subtype = 0;
4413 goto dostr;
4414 case CTLBACKQ:
4415 str = "$(...)";
4416 goto dostr;
4417 case CTLBACKQ+CTLQUOTE:
4418 str = "\"$(...)\"";
4419 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004420#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004421 case CTLARI:
4422 str = "$((";
4423 goto dostr;
4424 case CTLENDARI:
4425 str = "))";
4426 goto dostr;
4427#endif
4428 case CTLQUOTEMARK:
4429 quoted ^= 1;
4430 c = '"';
4431 break;
4432 case '=':
4433 if (subtype == 0)
4434 break;
4435 if ((subtype & VSTYPE) != VSNORMAL)
4436 quoted <<= 1;
4437 str = vstype[subtype & VSTYPE];
4438 if (subtype & VSNUL)
4439 c = ':';
4440 else
4441 goto checkstr;
4442 break;
4443 case '\'':
4444 case '\\':
4445 case '"':
4446 case '$':
4447 /* These can only happen inside quotes */
4448 cc[0] = c;
4449 str = cc;
4450 c = '\\';
4451 break;
4452 default:
4453 break;
4454 }
4455 USTPUTC(c, nextc);
4456 checkstr:
4457 if (!str)
4458 continue;
4459 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004460 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004461 USTPUTC(c, nextc);
4462 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004463 } /* while *p++ not NUL */
4464
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004465 if (quoted & 1) {
4466 USTPUTC('"', nextc);
4467 }
4468 *nextc = 0;
4469 cmdnextc = nextc;
4470}
4471
4472/* cmdtxt() and cmdlist() call each other */
4473static void cmdtxt(union node *n);
4474
4475static void
4476cmdlist(union node *np, int sep)
4477{
4478 for (; np; np = np->narg.next) {
4479 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004480 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004481 cmdtxt(np);
4482 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004483 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004484 }
4485}
4486
4487static void
4488cmdtxt(union node *n)
4489{
4490 union node *np;
4491 struct nodelist *lp;
4492 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493
4494 if (!n)
4495 return;
4496 switch (n->type) {
4497 default:
4498#if DEBUG
4499 abort();
4500#endif
4501 case NPIPE:
4502 lp = n->npipe.cmdlist;
4503 for (;;) {
4504 cmdtxt(lp->n);
4505 lp = lp->next;
4506 if (!lp)
4507 break;
4508 cmdputs(" | ");
4509 }
4510 break;
4511 case NSEMI:
4512 p = "; ";
4513 goto binop;
4514 case NAND:
4515 p = " && ";
4516 goto binop;
4517 case NOR:
4518 p = " || ";
4519 binop:
4520 cmdtxt(n->nbinary.ch1);
4521 cmdputs(p);
4522 n = n->nbinary.ch2;
4523 goto donode;
4524 case NREDIR:
4525 case NBACKGND:
4526 n = n->nredir.n;
4527 goto donode;
4528 case NNOT:
4529 cmdputs("!");
4530 n = n->nnot.com;
4531 donode:
4532 cmdtxt(n);
4533 break;
4534 case NIF:
4535 cmdputs("if ");
4536 cmdtxt(n->nif.test);
4537 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004538 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004539 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004540 cmdputs("; else ");
4541 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004542 } else {
4543 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004544 }
4545 p = "; fi";
4546 goto dotail;
4547 case NSUBSHELL:
4548 cmdputs("(");
4549 n = n->nredir.n;
4550 p = ")";
4551 goto dotail;
4552 case NWHILE:
4553 p = "while ";
4554 goto until;
4555 case NUNTIL:
4556 p = "until ";
4557 until:
4558 cmdputs(p);
4559 cmdtxt(n->nbinary.ch1);
4560 n = n->nbinary.ch2;
4561 p = "; done";
4562 dodo:
4563 cmdputs("; do ");
4564 dotail:
4565 cmdtxt(n);
4566 goto dotail2;
4567 case NFOR:
4568 cmdputs("for ");
4569 cmdputs(n->nfor.var);
4570 cmdputs(" in ");
4571 cmdlist(n->nfor.args, 1);
4572 n = n->nfor.body;
4573 p = "; done";
4574 goto dodo;
4575 case NDEFUN:
4576 cmdputs(n->narg.text);
4577 p = "() { ... }";
4578 goto dotail2;
4579 case NCMD:
4580 cmdlist(n->ncmd.args, 1);
4581 cmdlist(n->ncmd.redirect, 0);
4582 break;
4583 case NARG:
4584 p = n->narg.text;
4585 dotail2:
4586 cmdputs(p);
4587 break;
4588 case NHERE:
4589 case NXHERE:
4590 p = "<<...";
4591 goto dotail2;
4592 case NCASE:
4593 cmdputs("case ");
4594 cmdputs(n->ncase.expr->narg.text);
4595 cmdputs(" in ");
4596 for (np = n->ncase.cases; np; np = np->nclist.next) {
4597 cmdtxt(np->nclist.pattern);
4598 cmdputs(") ");
4599 cmdtxt(np->nclist.body);
4600 cmdputs(";; ");
4601 }
4602 p = "esac";
4603 goto dotail2;
4604 case NTO:
4605 p = ">";
4606 goto redir;
4607 case NCLOBBER:
4608 p = ">|";
4609 goto redir;
4610 case NAPPEND:
4611 p = ">>";
4612 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004613#if ENABLE_ASH_BASH_COMPAT
4614 case NTO2:
4615#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004616 case NTOFD:
4617 p = ">&";
4618 goto redir;
4619 case NFROM:
4620 p = "<";
4621 goto redir;
4622 case NFROMFD:
4623 p = "<&";
4624 goto redir;
4625 case NFROMTO:
4626 p = "<>";
4627 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004628 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004629 cmdputs(p);
4630 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004631 cmdputs(utoa(n->ndup.dupfd));
4632 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004633 }
4634 n = n->nfile.fname;
4635 goto donode;
4636 }
4637}
4638
4639static char *
4640commandtext(union node *n)
4641{
4642 char *name;
4643
4644 STARTSTACKSTR(cmdnextc);
4645 cmdtxt(n);
4646 name = stackblock();
4647 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4648 name, cmdnextc, cmdnextc));
4649 return ckstrdup(name);
4650}
4651#endif /* JOBS */
4652
4653/*
4654 * Fork off a subshell. If we are doing job control, give the subshell its
4655 * own process group. Jp is a job structure that the job is to be added to.
4656 * N is the command that will be evaluated by the child. Both jp and n may
4657 * be NULL. The mode parameter can be one of the following:
4658 * FORK_FG - Fork off a foreground process.
4659 * FORK_BG - Fork off a background process.
4660 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4661 * process group even if job control is on.
4662 *
4663 * When job control is turned off, background processes have their standard
4664 * input redirected to /dev/null (except for the second and later processes
4665 * in a pipeline).
4666 *
4667 * Called with interrupts off.
4668 */
4669/*
4670 * Clear traps on a fork.
4671 */
4672static void
4673clear_traps(void)
4674{
4675 char **tp;
4676
4677 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004678 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004679 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004680 if (trap_ptr == trap)
4681 free(*tp);
4682 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004683 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004684 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004685 setsignal(tp - trap);
4686 INT_ON;
4687 }
4688 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004689 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004690}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004691
4692/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004693static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004694
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004695/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004696static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004697forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004698{
4699 int oldlvl;
4700
4701 TRACE(("Child shell %d\n", getpid()));
4702 oldlvl = shlvl;
4703 shlvl++;
4704
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004705 /* man bash: "Non-builtin commands run by bash have signal handlers
4706 * set to the values inherited by the shell from its parent".
4707 * Do we do it correctly? */
4708
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004709 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004710
4711 if (mode == FORK_NOJOB /* is it `xxx` ? */
4712 && n && n->type == NCMD /* is it single cmd? */
4713 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004714 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004715 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4716 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4717 ) {
4718 TRACE(("Trap hack\n"));
4719 /* Awful hack for `trap` or $(trap).
4720 *
4721 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4722 * contains an example where "trap" is executed in a subshell:
4723 *
4724 * save_traps=$(trap)
4725 * ...
4726 * eval "$save_traps"
4727 *
4728 * Standard does not say that "trap" in subshell shall print
4729 * parent shell's traps. It only says that its output
4730 * must have suitable form, but then, in the above example
4731 * (which is not supposed to be normative), it implies that.
4732 *
4733 * bash (and probably other shell) does implement it
4734 * (traps are reset to defaults, but "trap" still shows them),
4735 * but as a result, "trap" logic is hopelessly messed up:
4736 *
4737 * # trap
4738 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4739 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4740 * # true | trap <--- trap is in subshell - no output (ditto)
4741 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4742 * trap -- 'echo Ho' SIGWINCH
4743 * # echo `(trap)` <--- in subshell in subshell - output
4744 * trap -- 'echo Ho' SIGWINCH
4745 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4746 * trap -- 'echo Ho' SIGWINCH
4747 *
4748 * The rules when to forget and when to not forget traps
4749 * get really complex and nonsensical.
4750 *
4751 * Our solution: ONLY bare $(trap) or `trap` is special.
4752 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004753 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004754 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004755 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004756 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004757 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004758#if JOBS
4759 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004760 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004761 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004762 pid_t pgrp;
4763
4764 if (jp->nprocs == 0)
4765 pgrp = getpid();
4766 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004767 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004768 /* this can fail because we are doing it in the parent also */
4769 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004770 if (mode == FORK_FG)
4771 xtcsetpgrp(ttyfd, pgrp);
4772 setsignal(SIGTSTP);
4773 setsignal(SIGTTOU);
4774 } else
4775#endif
4776 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004777 /* man bash: "When job control is not in effect,
4778 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004779 ignoresig(SIGINT);
4780 ignoresig(SIGQUIT);
4781 if (jp->nprocs == 0) {
4782 close(0);
4783 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004784 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004785 }
4786 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004787 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004788 if (iflag) { /* why if iflag only? */
4789 setsignal(SIGINT);
4790 setsignal(SIGTERM);
4791 }
4792 /* man bash:
4793 * "In all cases, bash ignores SIGQUIT. Non-builtin
4794 * commands run by bash have signal handlers
4795 * set to the values inherited by the shell
4796 * from its parent".
4797 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004798 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004799 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004800#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004801 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004802 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004803 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004804 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004805 /* "jobs": we do not want to clear job list for it,
4806 * instead we remove only _its_ own_ job from job list.
4807 * This makes "jobs .... | cat" more useful.
4808 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004809 freejob(curjob);
4810 return;
4811 }
4812#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004813 for (jp = curjob; jp; jp = jp->prev_job)
4814 freejob(jp);
4815 jobless = 0;
4816}
4817
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004818/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004819#if !JOBS
4820#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4821#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004822static void
4823forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4824{
4825 TRACE(("In parent shell: child = %d\n", pid));
4826 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004827 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4828 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004829 jobless++;
4830 return;
4831 }
4832#if JOBS
4833 if (mode != FORK_NOJOB && jp->jobctl) {
4834 int pgrp;
4835
4836 if (jp->nprocs == 0)
4837 pgrp = pid;
4838 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004839 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004840 /* This can fail because we are doing it in the child also */
4841 setpgid(pid, pgrp);
4842 }
4843#endif
4844 if (mode == FORK_BG) {
4845 backgndpid = pid; /* set $! */
4846 set_curjob(jp, CUR_RUNNING);
4847 }
4848 if (jp) {
4849 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004850 ps->ps_pid = pid;
4851 ps->ps_status = -1;
4852 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004853#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004854 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004855 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004856#endif
4857 }
4858}
4859
4860static int
4861forkshell(struct job *jp, union node *n, int mode)
4862{
4863 int pid;
4864
4865 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4866 pid = fork();
4867 if (pid < 0) {
4868 TRACE(("Fork failed, errno=%d", errno));
4869 if (jp)
4870 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004871 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004872 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004873 if (pid == 0) {
4874 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004875 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004876 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004877 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004878 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004879 return pid;
4880}
4881
4882/*
4883 * Wait for job to finish.
4884 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004885 * Under job control we have the problem that while a child process
4886 * is running interrupts generated by the user are sent to the child
4887 * but not to the shell. This means that an infinite loop started by
4888 * an interactive user may be hard to kill. With job control turned off,
4889 * an interactive user may place an interactive program inside a loop.
4890 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004891 * these interrupts to also abort the loop. The approach we take here
4892 * is to have the shell ignore interrupt signals while waiting for a
4893 * foreground process to terminate, and then send itself an interrupt
4894 * signal if the child process was terminated by an interrupt signal.
4895 * Unfortunately, some programs want to do a bit of cleanup and then
4896 * exit on interrupt; unless these processes terminate themselves by
4897 * sending a signal to themselves (instead of calling exit) they will
4898 * confuse this approach.
4899 *
4900 * Called with interrupts off.
4901 */
4902static int
4903waitforjob(struct job *jp)
4904{
4905 int st;
4906
4907 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004908
4909 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004910 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004911 /* In non-interactive shells, we _can_ get
4912 * a keyboard signal here and be EINTRed,
4913 * but we just loop back, waiting for command to complete.
4914 *
4915 * man bash:
4916 * "If bash is waiting for a command to complete and receives
4917 * a signal for which a trap has been set, the trap
4918 * will not be executed until the command completes."
4919 *
4920 * Reality is that even if trap is not set, bash
4921 * will not act on the signal until command completes.
4922 * Try this. sleep5intoff.c:
4923 * #include <signal.h>
4924 * #include <unistd.h>
4925 * int main() {
4926 * sigset_t set;
4927 * sigemptyset(&set);
4928 * sigaddset(&set, SIGINT);
4929 * sigaddset(&set, SIGQUIT);
4930 * sigprocmask(SIG_BLOCK, &set, NULL);
4931 * sleep(5);
4932 * return 0;
4933 * }
4934 * $ bash -c './sleep5intoff; echo hi'
4935 * ^C^C^C^C <--- pressing ^C once a second
4936 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004937 * $ bash -c './sleep5intoff; echo hi'
4938 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4939 * $ _
4940 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004941 dowait(DOWAIT_BLOCK, jp);
4942 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004943 INT_ON;
4944
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004945 st = getstatus(jp);
4946#if JOBS
4947 if (jp->jobctl) {
4948 xtcsetpgrp(ttyfd, rootpid);
4949 /*
4950 * This is truly gross.
4951 * If we're doing job control, then we did a TIOCSPGRP which
4952 * caused us (the shell) to no longer be in the controlling
4953 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4954 * intuit from the subprocess exit status whether a SIGINT
4955 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4956 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004957 if (jp->sigint) /* TODO: do the same with all signals */
4958 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004959 }
4960 if (jp->state == JOBDONE)
4961#endif
4962 freejob(jp);
4963 return st;
4964}
4965
4966/*
4967 * return 1 if there are stopped jobs, otherwise 0
4968 */
4969static int
4970stoppedjobs(void)
4971{
4972 struct job *jp;
4973 int retval;
4974
4975 retval = 0;
4976 if (job_warning)
4977 goto out;
4978 jp = curjob;
4979 if (jp && jp->state == JOBSTOPPED) {
4980 out2str("You have stopped jobs.\n");
4981 job_warning = 2;
4982 retval++;
4983 }
4984 out:
4985 return retval;
4986}
4987
4988
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004989/* ============ redir.c
4990 *
4991 * Code for dealing with input/output redirection.
4992 */
4993
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004994#undef EMPTY
4995#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004996#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004997#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004998
4999/*
5000 * Open a file in noclobber mode.
5001 * The code was copied from bash.
5002 */
5003static int
5004noclobberopen(const char *fname)
5005{
5006 int r, fd;
5007 struct stat finfo, finfo2;
5008
5009 /*
5010 * If the file exists and is a regular file, return an error
5011 * immediately.
5012 */
5013 r = stat(fname, &finfo);
5014 if (r == 0 && S_ISREG(finfo.st_mode)) {
5015 errno = EEXIST;
5016 return -1;
5017 }
5018
5019 /*
5020 * If the file was not present (r != 0), make sure we open it
5021 * exclusively so that if it is created before we open it, our open
5022 * will fail. Make sure that we do not truncate an existing file.
5023 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5024 * file was not a regular file, we leave O_EXCL off.
5025 */
5026 if (r != 0)
5027 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5028 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5029
5030 /* If the open failed, return the file descriptor right away. */
5031 if (fd < 0)
5032 return fd;
5033
5034 /*
5035 * OK, the open succeeded, but the file may have been changed from a
5036 * non-regular file to a regular file between the stat and the open.
5037 * We are assuming that the O_EXCL open handles the case where FILENAME
5038 * did not exist and is symlinked to an existing file between the stat
5039 * and open.
5040 */
5041
5042 /*
5043 * If we can open it and fstat the file descriptor, and neither check
5044 * revealed that it was a regular file, and the file has not been
5045 * replaced, return the file descriptor.
5046 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005047 if (fstat(fd, &finfo2) == 0
5048 && !S_ISREG(finfo2.st_mode)
5049 && finfo.st_dev == finfo2.st_dev
5050 && finfo.st_ino == finfo2.st_ino
5051 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005052 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005053 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005054
5055 /* The file has been replaced. badness. */
5056 close(fd);
5057 errno = EEXIST;
5058 return -1;
5059}
5060
5061/*
5062 * Handle here documents. Normally we fork off a process to write the
5063 * data to a pipe. If the document is short, we can stuff the data in
5064 * the pipe without forking.
5065 */
5066/* openhere needs this forward reference */
5067static void expandhere(union node *arg, int fd);
5068static int
5069openhere(union node *redir)
5070{
5071 int pip[2];
5072 size_t len = 0;
5073
5074 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005075 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005076 if (redir->type == NHERE) {
5077 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005078 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005079 full_write(pip[1], redir->nhere.doc->narg.text, len);
5080 goto out;
5081 }
5082 }
5083 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005084 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005085 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005086 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5087 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5088 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5089 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005090 signal(SIGPIPE, SIG_DFL);
5091 if (redir->type == NHERE)
5092 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005093 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005094 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005095 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005096 }
5097 out:
5098 close(pip[1]);
5099 return pip[0];
5100}
5101
5102static int
5103openredirect(union node *redir)
5104{
5105 char *fname;
5106 int f;
5107
5108 switch (redir->nfile.type) {
5109 case NFROM:
5110 fname = redir->nfile.expfname;
5111 f = open(fname, O_RDONLY);
5112 if (f < 0)
5113 goto eopen;
5114 break;
5115 case NFROMTO:
5116 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005117 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005118 if (f < 0)
5119 goto ecreate;
5120 break;
5121 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005122#if ENABLE_ASH_BASH_COMPAT
5123 case NTO2:
5124#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005125 /* Take care of noclobber mode. */
5126 if (Cflag) {
5127 fname = redir->nfile.expfname;
5128 f = noclobberopen(fname);
5129 if (f < 0)
5130 goto ecreate;
5131 break;
5132 }
5133 /* FALLTHROUGH */
5134 case NCLOBBER:
5135 fname = redir->nfile.expfname;
5136 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5137 if (f < 0)
5138 goto ecreate;
5139 break;
5140 case NAPPEND:
5141 fname = redir->nfile.expfname;
5142 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5143 if (f < 0)
5144 goto ecreate;
5145 break;
5146 default:
5147#if DEBUG
5148 abort();
5149#endif
5150 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005151/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005152// case NTOFD:
5153// case NFROMFD:
5154// f = -1;
5155// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005156 case NHERE:
5157 case NXHERE:
5158 f = openhere(redir);
5159 break;
5160 }
5161
5162 return f;
5163 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005164 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005165 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005166 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005167}
5168
5169/*
5170 * Copy a file descriptor to be >= to. Returns -1
5171 * if the source file descriptor is closed, EMPTY if there are no unused
5172 * file descriptors left.
5173 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005174/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5175 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005176enum {
5177 COPYFD_EXACT = (int)~(INT_MAX),
5178 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5179};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005180static int
5181copyfd(int from, int to)
5182{
5183 int newfd;
5184
Denis Vlasenko5a867312008-07-24 19:46:38 +00005185 if (to & COPYFD_EXACT) {
5186 to &= ~COPYFD_EXACT;
5187 /*if (from != to)*/
5188 newfd = dup2(from, to);
5189 } else {
5190 newfd = fcntl(from, F_DUPFD, to);
5191 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005192 if (newfd < 0) {
5193 if (errno == EMFILE)
5194 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005195 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005196 ash_msg_and_raise_error("%d: %m", from);
5197 }
5198 return newfd;
5199}
5200
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005201/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005202struct two_fd_t {
5203 int orig, copy;
5204};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005205struct redirtab {
5206 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005207 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005208 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005209 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005210};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005211#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005212
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005213static int need_to_remember(struct redirtab *rp, int fd)
5214{
5215 int i;
5216
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005217 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005218 return 0;
5219
5220 for (i = 0; i < rp->pair_count; i++) {
5221 if (rp->two_fd[i].orig == fd) {
5222 /* already remembered */
5223 return 0;
5224 }
5225 }
5226 return 1;
5227}
5228
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005229/* "hidden" fd is a fd used to read scripts, or a copy of such */
5230static int is_hidden_fd(struct redirtab *rp, int fd)
5231{
5232 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005233 struct parsefile *pf;
5234
5235 if (fd == -1)
5236 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005237 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005238 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005239 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005240 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005241 * $ ash # running ash interactively
5242 * $ . ./script.sh
5243 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005244 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005245 * it's still ok to use it: "read" builtin uses it,
5246 * why should we cripple "exec" builtin?
5247 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005248 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005249 return 1;
5250 }
5251 pf = pf->prev;
5252 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005253
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005254 if (!rp)
5255 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005256 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005257 fd |= COPYFD_RESTORE;
5258 for (i = 0; i < rp->pair_count; i++) {
5259 if (rp->two_fd[i].copy == fd) {
5260 return 1;
5261 }
5262 }
5263 return 0;
5264}
5265
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005266/*
5267 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5268 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005269 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005270 */
5271/* flags passed to redirect */
5272#define REDIR_PUSH 01 /* save previous values of file descriptors */
5273#define REDIR_SAVEFD2 03 /* set preverrout */
5274static void
5275redirect(union node *redir, int flags)
5276{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005277 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005278 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005279 int i;
5280 int fd;
5281 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005282 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005283
Denis Vlasenko01631112007-12-16 17:20:38 +00005284 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005285 if (!redir) {
5286 return;
5287 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005288
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005289 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005290 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005291 INT_OFF;
5292 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005293 union node *tmp = redir;
5294 do {
5295 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005296#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005297 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005298 sv_pos++;
5299#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005300 tmp = tmp->nfile.next;
5301 } while (tmp);
5302 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005303 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005304 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005305 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005306 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005307 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005308 while (sv_pos > 0) {
5309 sv_pos--;
5310 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5311 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005312 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005313
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005314 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005315 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005316 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005317 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005318 right_fd = redir->ndup.dupfd;
5319 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005320 /* redirect from/to same file descriptor? */
5321 if (right_fd == fd)
5322 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005323 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005324 if (is_hidden_fd(sv, right_fd)) {
5325 errno = EBADF; /* as if it is closed */
5326 ash_msg_and_raise_error("%d: %m", right_fd);
5327 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005328 newfd = -1;
5329 } else {
5330 newfd = openredirect(redir); /* always >= 0 */
5331 if (fd == newfd) {
5332 /* Descriptor wasn't open before redirect.
5333 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005334 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005335 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005336 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005337 continue;
5338 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005339 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005340#if ENABLE_ASH_BASH_COMPAT
5341 redirect_more:
5342#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005343 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005344 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005345 /* Careful to not accidentally "save"
5346 * to the same fd as right side fd in N>&M */
5347 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5348 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005349/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5350 * are closed in popredir() in the child, preventing them from leaking
5351 * into child. (popredir() also cleans up the mess in case of failures)
5352 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005353 if (i == -1) {
5354 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005355 if (i != EBADF) {
5356 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005357 if (newfd >= 0)
5358 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005359 errno = i;
5360 ash_msg_and_raise_error("%d: %m", fd);
5361 /* NOTREACHED */
5362 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005363 /* EBADF: it is not open - good, remember to close it */
5364 remember_to_close:
5365 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005366 } else { /* fd is open, save its copy */
5367 /* "exec fd>&-" should not close fds
5368 * which point to script file(s).
5369 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005370 if (is_hidden_fd(sv, fd))
5371 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005372 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005373 if (fd == 2)
5374 copied_fd2 = i;
5375 sv->two_fd[sv_pos].orig = fd;
5376 sv->two_fd[sv_pos].copy = i;
5377 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005378 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005379 if (newfd < 0) {
5380 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005381 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005382 /* Don't want to trigger debugging */
5383 if (fd != -1)
5384 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005385 } else {
5386 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005387 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005388 } else if (fd != newfd) { /* move newfd to fd */
5389 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005390#if ENABLE_ASH_BASH_COMPAT
5391 if (!(redir->nfile.type == NTO2 && fd == 2))
5392#endif
5393 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005394 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005395#if ENABLE_ASH_BASH_COMPAT
5396 if (redir->nfile.type == NTO2 && fd == 1) {
5397 /* We already redirected it to fd 1, now copy it to 2 */
5398 newfd = 1;
5399 fd = 2;
5400 goto redirect_more;
5401 }
5402#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005403 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005404
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005405 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005406 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5407 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005408}
5409
5410/*
5411 * Undo the effects of the last redirection.
5412 */
5413static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005414popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005415{
5416 struct redirtab *rp;
5417 int i;
5418
Denis Vlasenko01631112007-12-16 17:20:38 +00005419 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005420 return;
5421 INT_OFF;
5422 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005423 for (i = 0; i < rp->pair_count; i++) {
5424 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005425 int copy = rp->two_fd[i].copy;
5426 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005427 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005428 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005429 continue;
5430 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005431 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005432 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005433 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005434 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005435 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005436 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005437 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005438 }
5439 }
5440 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005441 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005442 free(rp);
5443 INT_ON;
5444}
5445
5446/*
5447 * Undo all redirections. Called on error or interrupt.
5448 */
5449
5450/*
5451 * Discard all saved file descriptors.
5452 */
5453static void
5454clearredir(int drop)
5455{
5456 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005457 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005458 if (!redirlist)
5459 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005460 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005461 }
5462}
5463
5464static int
5465redirectsafe(union node *redir, int flags)
5466{
5467 int err;
5468 volatile int saveint;
5469 struct jmploc *volatile savehandler = exception_handler;
5470 struct jmploc jmploc;
5471
5472 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005473 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5474 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005475 if (!err) {
5476 exception_handler = &jmploc;
5477 redirect(redir, flags);
5478 }
5479 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005480 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005481 longjmp(exception_handler->loc, 1);
5482 RESTORE_INT(saveint);
5483 return err;
5484}
5485
5486
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005487/* ============ Routines to expand arguments to commands
5488 *
5489 * We have to deal with backquotes, shell variables, and file metacharacters.
5490 */
5491
Mike Frysinger98c52642009-04-02 10:02:37 +00005492#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005493static arith_t
5494ash_arith(const char *s)
5495{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005496 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005497 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005498
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005499 math_state.lookupvar = lookupvar;
5500 math_state.setvar = setvar2;
5501 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005502
5503 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005504 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005505 if (math_state.errmsg)
5506 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005507 INT_ON;
5508
5509 return result;
5510}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005511#endif
5512
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005513/*
5514 * expandarg flags
5515 */
5516#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5517#define EXP_TILDE 0x2 /* do normal tilde expansion */
5518#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5519#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5520#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5521#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5522#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5523#define EXP_WORD 0x80 /* expand word in parameter expansion */
5524#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5525/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005526 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005527 */
5528#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5529#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5530#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5531#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5532#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5533
5534/*
5535 * Structure specifying which parts of the string should be searched
5536 * for IFS characters.
5537 */
5538struct ifsregion {
5539 struct ifsregion *next; /* next region in list */
5540 int begoff; /* offset of start of region */
5541 int endoff; /* offset of end of region */
5542 int nulonly; /* search for nul bytes only */
5543};
5544
5545struct arglist {
5546 struct strlist *list;
5547 struct strlist **lastp;
5548};
5549
5550/* output of current string */
5551static char *expdest;
5552/* list of back quote expressions */
5553static struct nodelist *argbackq;
5554/* first struct in list of ifs regions */
5555static struct ifsregion ifsfirst;
5556/* last struct in list */
5557static struct ifsregion *ifslastp;
5558/* holds expanded arg list */
5559static struct arglist exparg;
5560
5561/*
5562 * Our own itoa().
5563 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005564#if !ENABLE_SH_MATH_SUPPORT
5565/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5566typedef long arith_t;
5567# define ARITH_FMT "%ld"
5568#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569static int
5570cvtnum(arith_t num)
5571{
5572 int len;
5573
5574 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005575 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005576 STADJUST(len, expdest);
5577 return len;
5578}
5579
5580static size_t
5581esclen(const char *start, const char *p)
5582{
5583 size_t esc = 0;
5584
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005585 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005586 esc++;
5587 }
5588 return esc;
5589}
5590
5591/*
5592 * Remove any CTLESC characters from a string.
5593 */
5594static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005595rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005597 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005598
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005599 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005600 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005601 unsigned protect_against_glob;
5602 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005603
5604 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005605 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005606 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005607
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005608 q = p;
5609 r = str;
5610 if (flag & RMESCAPE_ALLOC) {
5611 size_t len = p - str;
5612 size_t fulllen = len + strlen(p) + 1;
5613
5614 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005615 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005616 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005617 /* p and str may be invalidated by makestrspace */
5618 str = (char *)stackblock() + strloc;
5619 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005620 } else if (flag & RMESCAPE_HEAP) {
5621 r = ckmalloc(fulllen);
5622 } else {
5623 r = stalloc(fulllen);
5624 }
5625 q = r;
5626 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005627 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005628 }
5629 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005630
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005631 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5632 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005633 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005634 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005635 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005636// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5637// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5638// Note: both inquotes and protect_against_glob only affect whether
5639// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005640 inquotes = ~inquotes;
5641 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005642 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005643 continue;
5644 }
5645 if (*p == '\\') {
5646 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005647 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648 goto copy;
5649 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005650 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005651 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005652 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005653 *q++ = '\\';
5654 }
5655 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005656 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005657 copy:
5658 *q++ = *p++;
5659 }
5660 *q = '\0';
5661 if (flag & RMESCAPE_GROW) {
5662 expdest = r;
5663 STADJUST(q - r + 1, expdest);
5664 }
5665 return r;
5666}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005667#define pmatch(a, b) !fnmatch((a), (b), 0)
5668
5669/*
5670 * Prepare a pattern for a expmeta (internal glob(3)) call.
5671 *
5672 * Returns an stalloced string.
5673 */
5674static char *
5675preglob(const char *pattern, int quoted, int flag)
5676{
5677 flag |= RMESCAPE_GLOB;
5678 if (quoted) {
5679 flag |= RMESCAPE_QUOTED;
5680 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005681 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005682}
5683
5684/*
5685 * Put a string on the stack.
5686 */
5687static void
5688memtodest(const char *p, size_t len, int syntax, int quotes)
5689{
5690 char *q = expdest;
5691
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005692 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005693
5694 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005695 unsigned char c = *p++;
5696 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005697 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005698 if (quotes) {
5699 int n = SIT(c, syntax);
5700 if (n == CCTL || n == CBACK)
5701 USTPUTC(CTLESC, q);
5702 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005703 USTPUTC(c, q);
5704 }
5705
5706 expdest = q;
5707}
5708
5709static void
5710strtodest(const char *p, int syntax, int quotes)
5711{
5712 memtodest(p, strlen(p), syntax, quotes);
5713}
5714
5715/*
5716 * Record the fact that we have to scan this region of the
5717 * string for IFS characters.
5718 */
5719static void
5720recordregion(int start, int end, int nulonly)
5721{
5722 struct ifsregion *ifsp;
5723
5724 if (ifslastp == NULL) {
5725 ifsp = &ifsfirst;
5726 } else {
5727 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005728 ifsp = ckzalloc(sizeof(*ifsp));
5729 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005730 ifslastp->next = ifsp;
5731 INT_ON;
5732 }
5733 ifslastp = ifsp;
5734 ifslastp->begoff = start;
5735 ifslastp->endoff = end;
5736 ifslastp->nulonly = nulonly;
5737}
5738
5739static void
5740removerecordregions(int endoff)
5741{
5742 if (ifslastp == NULL)
5743 return;
5744
5745 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005746 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005747 struct ifsregion *ifsp;
5748 INT_OFF;
5749 ifsp = ifsfirst.next->next;
5750 free(ifsfirst.next);
5751 ifsfirst.next = ifsp;
5752 INT_ON;
5753 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005754 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005755 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005756 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005757 ifslastp = &ifsfirst;
5758 ifsfirst.endoff = endoff;
5759 }
5760 return;
5761 }
5762
5763 ifslastp = &ifsfirst;
5764 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005765 ifslastp = ifslastp->next;
5766 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005767 struct ifsregion *ifsp;
5768 INT_OFF;
5769 ifsp = ifslastp->next->next;
5770 free(ifslastp->next);
5771 ifslastp->next = ifsp;
5772 INT_ON;
5773 }
5774 if (ifslastp->endoff > endoff)
5775 ifslastp->endoff = endoff;
5776}
5777
5778static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005779exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005780{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005781 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005782 char *name;
5783 struct passwd *pw;
5784 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005785 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005786 int startloc;
5787
5788 name = p + 1;
5789
5790 while ((c = *++p) != '\0') {
5791 switch (c) {
5792 case CTLESC:
5793 return startp;
5794 case CTLQUOTEMARK:
5795 return startp;
5796 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005797 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005798 goto done;
5799 break;
5800 case '/':
5801 case CTLENDVAR:
5802 goto done;
5803 }
5804 }
5805 done:
5806 *p = '\0';
5807 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005808 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005809 } else {
5810 pw = getpwnam(name);
5811 if (pw == NULL)
5812 goto lose;
5813 home = pw->pw_dir;
5814 }
5815 if (!home || !*home)
5816 goto lose;
5817 *p = c;
5818 startloc = expdest - (char *)stackblock();
5819 strtodest(home, SQSYNTAX, quotes);
5820 recordregion(startloc, expdest - (char *)stackblock(), 0);
5821 return p;
5822 lose:
5823 *p = c;
5824 return startp;
5825}
5826
5827/*
5828 * Execute a command inside back quotes. If it's a builtin command, we
5829 * want to save its output in a block obtained from malloc. Otherwise
5830 * we fork off a subprocess and get the output of the command via a pipe.
5831 * Should be called with interrupts off.
5832 */
5833struct backcmd { /* result of evalbackcmd */
5834 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005835 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005836 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005837 struct job *jp; /* job structure for command */
5838};
5839
5840/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005841static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005842#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005843static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005844
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005845static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005846evalbackcmd(union node *n, struct backcmd *result)
5847{
5848 int saveherefd;
5849
5850 result->fd = -1;
5851 result->buf = NULL;
5852 result->nleft = 0;
5853 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005854 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005855 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005856
5857 saveherefd = herefd;
5858 herefd = -1;
5859
5860 {
5861 int pip[2];
5862 struct job *jp;
5863
5864 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005865 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005866 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005867 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5868 FORCE_INT_ON;
5869 close(pip[0]);
5870 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005871 /*close(1);*/
5872 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005873 close(pip[1]);
5874 }
5875 eflag = 0;
5876 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5877 /* NOTREACHED */
5878 }
5879 close(pip[1]);
5880 result->fd = pip[0];
5881 result->jp = jp;
5882 }
5883 herefd = saveherefd;
5884 out:
5885 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5886 result->fd, result->buf, result->nleft, result->jp));
5887}
5888
5889/*
5890 * Expand stuff in backwards quotes.
5891 */
5892static void
5893expbackq(union node *cmd, int quoted, int quotes)
5894{
5895 struct backcmd in;
5896 int i;
5897 char buf[128];
5898 char *p;
5899 char *dest;
5900 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005901 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005902 struct stackmark smark;
5903
5904 INT_OFF;
5905 setstackmark(&smark);
5906 dest = expdest;
5907 startloc = dest - (char *)stackblock();
5908 grabstackstr(dest);
5909 evalbackcmd(cmd, &in);
5910 popstackmark(&smark);
5911
5912 p = in.buf;
5913 i = in.nleft;
5914 if (i == 0)
5915 goto read;
5916 for (;;) {
5917 memtodest(p, i, syntax, quotes);
5918 read:
5919 if (in.fd < 0)
5920 break;
Denys Vlasenko80542ba2011-05-08 21:23:43 +02005921 i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005922 TRACE(("expbackq: read returns %d\n", i));
5923 if (i <= 0)
5924 break;
5925 p = buf;
5926 }
5927
Denis Vlasenko60818682007-09-28 22:07:23 +00005928 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005929 if (in.fd >= 0) {
5930 close(in.fd);
5931 back_exitstatus = waitforjob(in.jp);
5932 }
5933 INT_ON;
5934
5935 /* Eat all trailing newlines */
5936 dest = expdest;
5937 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5938 STUNPUTC(dest);
5939 expdest = dest;
5940
5941 if (quoted == 0)
5942 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005943 TRACE(("evalbackq: size:%d:'%.*s'\n",
5944 (int)((dest - (char *)stackblock()) - startloc),
5945 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005946 stackblock() + startloc));
5947}
5948
Mike Frysinger98c52642009-04-02 10:02:37 +00005949#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005950/*
5951 * Expand arithmetic expression. Backup to start of expression,
5952 * evaluate, place result in (backed up) result, adjust string position.
5953 */
5954static void
5955expari(int quotes)
5956{
5957 char *p, *start;
5958 int begoff;
5959 int flag;
5960 int len;
5961
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005962 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005963
5964 /*
5965 * This routine is slightly over-complicated for
5966 * efficiency. Next we scan backwards looking for the
5967 * start of arithmetic.
5968 */
5969 start = stackblock();
5970 p = expdest - 1;
5971 *p = '\0';
5972 p--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005973 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005974 int esc;
5975
Denys Vlasenkocd716832009-11-28 22:14:02 +01005976 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005977 p--;
5978#if DEBUG
5979 if (p < start) {
5980 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5981 }
5982#endif
5983 }
5984
5985 esc = esclen(start, p);
5986 if (!(esc % 2)) {
5987 break;
5988 }
5989
5990 p -= esc + 1;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005991 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005992
5993 begoff = p - start;
5994
5995 removerecordregions(begoff);
5996
5997 flag = p[1];
5998
5999 expdest = p;
6000
6001 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006002 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006003
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006004 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006005
6006 if (flag != '"')
6007 recordregion(begoff, begoff + len, 0);
6008}
6009#endif
6010
6011/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006012static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006013
6014/*
6015 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6016 * characters to allow for further processing. Otherwise treat
6017 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006018 *
6019 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6020 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6021 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006022 */
6023static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006024argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006026 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006027 '=',
6028 ':',
6029 CTLQUOTEMARK,
6030 CTLENDVAR,
6031 CTLESC,
6032 CTLVAR,
6033 CTLBACKQ,
6034 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006035#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036 CTLENDARI,
6037#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006038 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006039 };
6040 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006041 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6042 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 int inquotes;
6044 size_t length;
6045 int startloc;
6046
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006047 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006049 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006050 reject++;
6051 }
6052 inquotes = 0;
6053 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006054 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006055 char *q;
6056
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006057 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006058 tilde:
6059 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006060 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006061 q++;
6062 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006063 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006064 }
6065 start:
6066 startloc = expdest - (char *)stackblock();
6067 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006068 unsigned char c;
6069
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006070 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006071 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006072 if (c) {
6073 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006074 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006075 ) {
6076 /* c == '=' || c == ':' || c == CTLENDARI */
6077 length++;
6078 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006079 }
6080 if (length > 0) {
6081 int newloc;
6082 expdest = stack_nputstr(p, length, expdest);
6083 newloc = expdest - (char *)stackblock();
6084 if (breakall && !inquotes && newloc > startloc) {
6085 recordregion(startloc, newloc, 0);
6086 }
6087 startloc = newloc;
6088 }
6089 p += length + 1;
6090 length = 0;
6091
6092 switch (c) {
6093 case '\0':
6094 goto breakloop;
6095 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006096 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006097 p--;
6098 continue;
6099 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006100 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006101 reject++;
6102 /* fall through */
6103 case ':':
6104 /*
6105 * sort of a hack - expand tildes in variable
6106 * assignments (after the first '=' and after ':'s).
6107 */
6108 if (*--p == '~') {
6109 goto tilde;
6110 }
6111 continue;
6112 }
6113
6114 switch (c) {
6115 case CTLENDVAR: /* ??? */
6116 goto breakloop;
6117 case CTLQUOTEMARK:
6118 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006119 if (!inquotes
6120 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006121 && ( p[4] == (char)CTLQUOTEMARK
6122 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006123 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006124 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006125 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006126 goto start;
6127 }
6128 inquotes = !inquotes;
6129 addquote:
6130 if (quotes) {
6131 p--;
6132 length++;
6133 startloc++;
6134 }
6135 break;
6136 case CTLESC:
6137 startloc++;
6138 length++;
6139 goto addquote;
6140 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006141 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006142 goto start;
6143 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006144 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006145 case CTLBACKQ|CTLQUOTE:
6146 expbackq(argbackq->n, c, quotes);
6147 argbackq = argbackq->next;
6148 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006149#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006150 case CTLENDARI:
6151 p--;
6152 expari(quotes);
6153 goto start;
6154#endif
6155 }
6156 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006157 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006158}
6159
6160static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006161scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6162 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006163{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006164 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006165 char c;
6166
6167 loc = startp;
6168 loc2 = rmesc;
6169 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006170 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006171 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006172
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006173 c = *loc2;
6174 if (zero) {
6175 *loc2 = '\0';
6176 s = rmesc;
6177 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006178 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006179
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006180 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006181 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006183 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006184 loc++;
6185 loc++;
6186 loc2++;
6187 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006188 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006189}
6190
6191static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006192scanright(char *startp, char *rmesc, char *rmescend,
6193 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006194{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006195#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6196 int try2optimize = match_at_start;
6197#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006198 int esc = 0;
6199 char *loc;
6200 char *loc2;
6201
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006202 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6203 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6204 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6205 * Logic:
6206 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6207 * and on each iteration they go back two/one char until they reach the beginning.
6208 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6209 */
6210 /* TODO: document in what other circumstances we are called. */
6211
6212 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006213 int match;
6214 char c = *loc2;
6215 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006216 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006217 *loc2 = '\0';
6218 s = rmesc;
6219 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006220 match = pmatch(pattern, s);
6221 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222 *loc2 = c;
6223 if (match)
6224 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006225#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6226 if (try2optimize) {
6227 /* Maybe we can optimize this:
6228 * if pattern ends with unescaped *, we can avoid checking
6229 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6230 * it wont match truncated "raw_value_of_" strings too.
6231 */
6232 unsigned plen = strlen(pattern);
6233 /* Does it end with "*"? */
6234 if (plen != 0 && pattern[--plen] == '*') {
6235 /* "xxxx*" is not escaped */
6236 /* "xxx\*" is escaped */
6237 /* "xx\\*" is not escaped */
6238 /* "x\\\*" is escaped */
6239 int slashes = 0;
6240 while (plen != 0 && pattern[--plen] == '\\')
6241 slashes++;
6242 if (!(slashes & 1))
6243 break; /* ends with unescaped "*" */
6244 }
6245 try2optimize = 0;
6246 }
6247#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006248 loc--;
6249 if (quotes) {
6250 if (--esc < 0) {
6251 esc = esclen(startp, loc);
6252 }
6253 if (esc % 2) {
6254 esc--;
6255 loc--;
6256 }
6257 }
6258 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006259 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006260}
6261
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006262static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006263static void
6264varunset(const char *end, const char *var, const char *umsg, int varflags)
6265{
6266 const char *msg;
6267 const char *tail;
6268
6269 tail = nullstr;
6270 msg = "parameter not set";
6271 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006272 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273 if (varflags & VSNUL)
6274 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006275 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006276 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006277 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006278 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006279 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006280}
6281
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006282#if ENABLE_ASH_BASH_COMPAT
6283static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006284parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006285{
6286 char *idx, *repl = NULL;
6287 unsigned char c;
6288
Denys Vlasenko16149002010-08-06 22:06:21 +02006289 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006290 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006291 idx = arg;
6292 while (1) {
6293 c = *arg;
6294 if (!c)
6295 break;
6296 if (c == '/') {
6297 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006298 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006299 repl = idx + 1;
6300 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006301 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006302 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006303 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006304 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006305 /*
6306 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6307 * The result is a_\_z_c (not a\_\_z_c)!
6308 *
6309 * Enable debug prints in this function and you'll see:
6310 * ash: arg:'\\b/_\\_z_' varflags:d
6311 * ash: pattern:'\\b' repl:'_\_z_'
6312 * That is, \\b is interpreted as \\b, but \\_ as \_!
6313 * IOW: search pattern and replace string treat backslashes
6314 * differently! That is the reason why we check repl below:
6315 */
6316 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6317 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006318 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006319 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006320 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006321
6322 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006323}
6324#endif /* ENABLE_ASH_BASH_COMPAT */
6325
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006327subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006328 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006330 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006331 char *startp;
6332 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006333 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006334 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006335 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006336 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006337 int saveherefd = herefd;
6338 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006339 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006340 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006341
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006342 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6343 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006344
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006345 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006346 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6347 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006348 STPUTC('\0', expdest);
6349 herefd = saveherefd;
6350 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006351 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006352
6353 switch (subtype) {
6354 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006355 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356 amount = startp - expdest;
6357 STADJUST(amount, expdest);
6358 return startp;
6359
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006360 case VSQUESTION:
6361 varunset(p, varname, startp, varflags);
6362 /* NOTREACHED */
6363
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006364#if ENABLE_ASH_BASH_COMPAT
6365 case VSSUBSTR:
6366 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006367 /* Read POS in ${var:POS:LEN} */
6368 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006369 len = str - startp - 1;
6370
6371 /* *loc != '\0', guaranteed by parser */
6372 if (quotes) {
6373 char *ptr;
6374
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006375 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006376 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006377 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006378 len--;
6379 ptr++;
6380 }
6381 }
6382 }
6383 orig_len = len;
6384
6385 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006386 /* ${var::LEN} */
6387 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006388 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006389 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006390 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006391 while (*loc && *loc != ':') {
6392 /* TODO?
6393 * bash complains on: var=qwe; echo ${var:1a:123}
6394 if (!isdigit(*loc))
6395 ash_msg_and_raise_error(msg_illnum, str);
6396 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006397 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006398 }
6399 if (*loc++ == ':') {
6400 len = number(loc);
6401 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006402 }
6403 if (pos >= orig_len) {
6404 pos = 0;
6405 len = 0;
6406 }
6407 if (len > (orig_len - pos))
6408 len = orig_len - pos;
6409
6410 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006411 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006412 str++;
6413 }
6414 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006415 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006416 *loc++ = *str++;
6417 *loc++ = *str++;
6418 }
6419 *loc = '\0';
6420 amount = loc - expdest;
6421 STADJUST(amount, expdest);
6422 return loc;
6423#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006424 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006425
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006426 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006427
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006428 /* We'll comeback here if we grow the stack while handling
6429 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6430 * stack will need rebasing, and we'll need to remove our work
6431 * areas each time
6432 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006433 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006434
6435 amount = expdest - ((char *)stackblock() + resetloc);
6436 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006437 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006438
6439 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006440 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006442 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 if (rmesc != startp) {
6444 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006445 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006446 }
6447 }
6448 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006449 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006450 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006451 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006452
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006453#if ENABLE_ASH_BASH_COMPAT
6454 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006455 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006456
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006457 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006458 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006459 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006460 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006461 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006462 }
6463
6464 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006465 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006466 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006467
6468 len = 0;
6469 idx = startp;
6470 end = str - 1;
6471 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006472 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006473 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006474 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006475 if (!loc) {
6476 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006477 char *restart_detect = stackblock();
6478 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006479 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006480 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006481 idx++;
6482 len++;
6483 STPUTC(*idx, expdest);
6484 }
6485 if (stackblock() != restart_detect)
6486 goto restart;
6487 idx++;
6488 len++;
6489 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006490 /* continue; - prone to quadratic behavior, smarter code: */
6491 if (idx >= end)
6492 break;
6493 if (str[0] == '*') {
6494 /* Pattern is "*foo". If "*foo" does not match "long_string",
6495 * it would never match "ong_string" etc, no point in trying.
6496 */
6497 goto skip_matching;
6498 }
6499 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006500 }
6501
6502 if (subtype == VSREPLACEALL) {
6503 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006504 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006505 idx++;
6506 idx++;
6507 rmesc++;
6508 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006509 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006510 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006511 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006512
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006513 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006514 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006515 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006516 if (quotes && *loc == '\\') {
6517 STPUTC(CTLESC, expdest);
6518 len++;
6519 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006520 STPUTC(*loc, expdest);
6521 if (stackblock() != restart_detect)
6522 goto restart;
6523 len++;
6524 }
6525
6526 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006527 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006528 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006529 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006530 STPUTC(*idx, expdest);
6531 if (stackblock() != restart_detect)
6532 goto restart;
6533 len++;
6534 idx++;
6535 }
6536 break;
6537 }
6538 }
6539
6540 /* We've put the replaced text into a buffer at workloc, now
6541 * move it to the right place and adjust the stack.
6542 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006543 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006544 startp = (char *)stackblock() + startloc;
6545 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006546 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006547 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006548 STADJUST(-amount, expdest);
6549 return startp;
6550 }
6551#endif /* ENABLE_ASH_BASH_COMPAT */
6552
6553 subtype -= VSTRIMRIGHT;
6554#if DEBUG
6555 if (subtype < 0 || subtype > 7)
6556 abort();
6557#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006558 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006559 zero = subtype >> 1;
6560 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6561 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6562
6563 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6564 if (loc) {
6565 if (zero) {
6566 memmove(startp, loc, str - loc);
6567 loc = startp + (str - loc) - 1;
6568 }
6569 *loc = '\0';
6570 amount = loc - expdest;
6571 STADJUST(amount, expdest);
6572 }
6573 return loc;
6574}
6575
6576/*
6577 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006578 * name parameter (examples):
6579 * ash -c 'echo $1' name:'1='
6580 * ash -c 'echo $qwe' name:'qwe='
6581 * ash -c 'echo $$' name:'$='
6582 * ash -c 'echo ${$}' name:'$='
6583 * ash -c 'echo ${$##q}' name:'$=q'
6584 * ash -c 'echo ${#$}' name:'$='
6585 * note: examples with bad shell syntax:
6586 * ash -c 'echo ${#$1}' name:'$=1'
6587 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006589static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006590varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006591{
Mike Frysinger98c52642009-04-02 10:02:37 +00006592 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006593 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006595 int sepq = 0;
6596 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006597 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006598 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006599 int quoted = varflags & VSQUOTE;
6600 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006601
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006602 switch (*name) {
6603 case '$':
6604 num = rootpid;
6605 goto numvar;
6606 case '?':
6607 num = exitstatus;
6608 goto numvar;
6609 case '#':
6610 num = shellparam.nparam;
6611 goto numvar;
6612 case '!':
6613 num = backgndpid;
6614 if (num == 0)
6615 return -1;
6616 numvar:
6617 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006618 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006619 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006620 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006621 for (i = NOPTS - 1; i >= 0; i--) {
6622 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006623 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006624 len++;
6625 }
6626 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006627 check_1char_name:
6628#if 0
6629 /* handles cases similar to ${#$1} */
6630 if (name[2] != '\0')
6631 raise_error_syntax("bad substitution");
6632#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006633 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006634 case '@': {
6635 char **ap;
6636 int sep;
6637
6638 if (quoted && (flags & EXP_FULL)) {
6639 /* note: this is not meant as PEOF value */
6640 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006641 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006642 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006643 /* fall through */
6644 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006645 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006646 i = SIT(sep, syntax);
6647 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006648 sepq = 1;
6649 param:
6650 ap = shellparam.p;
6651 if (!ap)
6652 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006653 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006654 size_t partlen;
6655
6656 partlen = strlen(p);
6657 len += partlen;
6658
6659 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6660 memtodest(p, partlen, syntax, quotes);
6661
6662 if (*ap && sep) {
6663 char *q;
6664
6665 len++;
6666 if (subtype == VSPLUS || subtype == VSLENGTH) {
6667 continue;
6668 }
6669 q = expdest;
6670 if (sepq)
6671 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006672 /* note: may put NUL despite sep != 0
6673 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006674 STPUTC(sep, q);
6675 expdest = q;
6676 }
6677 }
6678 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006679 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006680 case '0':
6681 case '1':
6682 case '2':
6683 case '3':
6684 case '4':
6685 case '5':
6686 case '6':
6687 case '7':
6688 case '8':
6689 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006690 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006691 if (num < 0 || num > shellparam.nparam)
6692 return -1;
6693 p = num ? shellparam.p[num - 1] : arg0;
6694 goto value;
6695 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006696 /* NB: name has form "VAR=..." */
6697
6698 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6699 * which should be considered before we check variables. */
6700 if (var_str_list) {
6701 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6702 p = NULL;
6703 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006704 char *str, *eq;
6705 str = var_str_list->text;
6706 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006707 if (!eq) /* stop at first non-assignment */
6708 break;
6709 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006710 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006711 && strncmp(str, name, name_len) == 0
6712 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006713 p = eq;
6714 /* goto value; - WRONG! */
6715 /* think "A=1 A=2 B=$A" */
6716 }
6717 var_str_list = var_str_list->next;
6718 } while (var_str_list);
6719 if (p)
6720 goto value;
6721 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006722 p = lookupvar(name);
6723 value:
6724 if (!p)
6725 return -1;
6726
6727 len = strlen(p);
6728 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6729 memtodest(p, len, syntax, quotes);
6730 return len;
6731 }
6732
6733 if (subtype == VSPLUS || subtype == VSLENGTH)
6734 STADJUST(-len, expdest);
6735 return len;
6736}
6737
6738/*
6739 * Expand a variable, and return a pointer to the next character in the
6740 * input string.
6741 */
6742static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006743evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006744{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006745 char varflags;
6746 char subtype;
6747 char quoted;
6748 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006749 char *var;
6750 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006751 int startloc;
6752 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006753
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006754 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006755 subtype = varflags & VSTYPE;
6756 quoted = varflags & VSQUOTE;
6757 var = p;
6758 easy = (!quoted || (*var == '@' && shellparam.nparam));
6759 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006760 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006761
6762 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006763 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006764 if (varflags & VSNUL)
6765 varlen--;
6766
6767 if (subtype == VSPLUS) {
6768 varlen = -1 - varlen;
6769 goto vsplus;
6770 }
6771
6772 if (subtype == VSMINUS) {
6773 vsplus:
6774 if (varlen < 0) {
6775 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006776 p,
6777 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006778 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006779 );
6780 goto end;
6781 }
6782 if (easy)
6783 goto record;
6784 goto end;
6785 }
6786
6787 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6788 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006789 if (subevalvar(p, var, /* strloc: */ 0,
6790 subtype, startloc, varflags,
6791 /* quotes: */ 0,
6792 var_str_list)
6793 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006794 varflags &= ~VSNUL;
6795 /*
6796 * Remove any recorded regions beyond
6797 * start of variable
6798 */
6799 removerecordregions(startloc);
6800 goto again;
6801 }
6802 goto end;
6803 }
6804 if (easy)
6805 goto record;
6806 goto end;
6807 }
6808
6809 if (varlen < 0 && uflag)
6810 varunset(p, var, 0, 0);
6811
6812 if (subtype == VSLENGTH) {
6813 cvtnum(varlen > 0 ? varlen : 0);
6814 goto record;
6815 }
6816
6817 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006818 if (easy)
6819 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006820 goto end;
6821 }
6822
6823#if DEBUG
6824 switch (subtype) {
6825 case VSTRIMLEFT:
6826 case VSTRIMLEFTMAX:
6827 case VSTRIMRIGHT:
6828 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006829#if ENABLE_ASH_BASH_COMPAT
6830 case VSSUBSTR:
6831 case VSREPLACE:
6832 case VSREPLACEALL:
6833#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006834 break;
6835 default:
6836 abort();
6837 }
6838#endif
6839
6840 if (varlen >= 0) {
6841 /*
6842 * Terminate the string and start recording the pattern
6843 * right after it
6844 */
6845 STPUTC('\0', expdest);
6846 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006847 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006848 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006849//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006850 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006851 var_str_list)
6852 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006853 int amount = expdest - (
6854 (char *)stackblock() + patloc - 1
6855 );
6856 STADJUST(-amount, expdest);
6857 }
6858 /* Remove any recorded regions beyond start of variable */
6859 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006860 record:
6861 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006862 }
6863
6864 end:
6865 if (subtype != VSNORMAL) { /* skip to end of alternative */
6866 int nesting = 1;
6867 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006868 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006869 if (c == CTLESC)
6870 p++;
6871 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6872 if (varlen >= 0)
6873 argbackq = argbackq->next;
6874 } else if (c == CTLVAR) {
6875 if ((*p++ & VSTYPE) != VSNORMAL)
6876 nesting++;
6877 } else if (c == CTLENDVAR) {
6878 if (--nesting == 0)
6879 break;
6880 }
6881 }
6882 }
6883 return p;
6884}
6885
6886/*
6887 * Break the argument string into pieces based upon IFS and add the
6888 * strings to the argument list. The regions of the string to be
6889 * searched for IFS characters have been stored by recordregion.
6890 */
6891static void
6892ifsbreakup(char *string, struct arglist *arglist)
6893{
6894 struct ifsregion *ifsp;
6895 struct strlist *sp;
6896 char *start;
6897 char *p;
6898 char *q;
6899 const char *ifs, *realifs;
6900 int ifsspc;
6901 int nulonly;
6902
6903 start = string;
6904 if (ifslastp != NULL) {
6905 ifsspc = 0;
6906 nulonly = 0;
6907 realifs = ifsset() ? ifsval() : defifs;
6908 ifsp = &ifsfirst;
6909 do {
6910 p = string + ifsp->begoff;
6911 nulonly = ifsp->nulonly;
6912 ifs = nulonly ? nullstr : realifs;
6913 ifsspc = 0;
6914 while (p < string + ifsp->endoff) {
6915 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006916 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006917 p++;
6918 if (!strchr(ifs, *p)) {
6919 p++;
6920 continue;
6921 }
6922 if (!nulonly)
6923 ifsspc = (strchr(defifs, *p) != NULL);
6924 /* Ignore IFS whitespace at start */
6925 if (q == start && ifsspc) {
6926 p++;
6927 start = p;
6928 continue;
6929 }
6930 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006931 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006932 sp->text = start;
6933 *arglist->lastp = sp;
6934 arglist->lastp = &sp->next;
6935 p++;
6936 if (!nulonly) {
6937 for (;;) {
6938 if (p >= string + ifsp->endoff) {
6939 break;
6940 }
6941 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006942 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006943 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006944 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006945 p = q;
6946 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006947 }
6948 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006949 if (ifsspc) {
6950 p++;
6951 ifsspc = 0;
6952 } else {
6953 p = q;
6954 break;
6955 }
6956 } else
6957 p++;
6958 }
6959 }
6960 start = p;
6961 } /* while */
6962 ifsp = ifsp->next;
6963 } while (ifsp != NULL);
6964 if (nulonly)
6965 goto add;
6966 }
6967
6968 if (!*start)
6969 return;
6970
6971 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006972 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006973 sp->text = start;
6974 *arglist->lastp = sp;
6975 arglist->lastp = &sp->next;
6976}
6977
6978static void
6979ifsfree(void)
6980{
6981 struct ifsregion *p;
6982
6983 INT_OFF;
6984 p = ifsfirst.next;
6985 do {
6986 struct ifsregion *ifsp;
6987 ifsp = p->next;
6988 free(p);
6989 p = ifsp;
6990 } while (p);
6991 ifslastp = NULL;
6992 ifsfirst.next = NULL;
6993 INT_ON;
6994}
6995
6996/*
6997 * Add a file name to the list.
6998 */
6999static void
7000addfname(const char *name)
7001{
7002 struct strlist *sp;
7003
Denis Vlasenko597906c2008-02-20 16:38:54 +00007004 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007005 sp->text = ststrdup(name);
7006 *exparg.lastp = sp;
7007 exparg.lastp = &sp->next;
7008}
7009
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007010/*
7011 * Do metacharacter (i.e. *, ?, [...]) expansion.
7012 */
7013static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007014expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007015{
7016 char *p;
7017 const char *cp;
7018 char *start;
7019 char *endname;
7020 int metaflag;
7021 struct stat statb;
7022 DIR *dirp;
7023 struct dirent *dp;
7024 int atend;
7025 int matchdot;
7026
7027 metaflag = 0;
7028 start = name;
7029 for (p = name; *p; p++) {
7030 if (*p == '*' || *p == '?')
7031 metaflag = 1;
7032 else if (*p == '[') {
7033 char *q = p + 1;
7034 if (*q == '!')
7035 q++;
7036 for (;;) {
7037 if (*q == '\\')
7038 q++;
7039 if (*q == '/' || *q == '\0')
7040 break;
7041 if (*++q == ']') {
7042 metaflag = 1;
7043 break;
7044 }
7045 }
7046 } else if (*p == '\\')
7047 p++;
7048 else if (*p == '/') {
7049 if (metaflag)
7050 goto out;
7051 start = p + 1;
7052 }
7053 }
7054 out:
7055 if (metaflag == 0) { /* we've reached the end of the file name */
7056 if (enddir != expdir)
7057 metaflag++;
7058 p = name;
7059 do {
7060 if (*p == '\\')
7061 p++;
7062 *enddir++ = *p;
7063 } while (*p++);
7064 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7065 addfname(expdir);
7066 return;
7067 }
7068 endname = p;
7069 if (name < start) {
7070 p = name;
7071 do {
7072 if (*p == '\\')
7073 p++;
7074 *enddir++ = *p++;
7075 } while (p < start);
7076 }
7077 if (enddir == expdir) {
7078 cp = ".";
7079 } else if (enddir == expdir + 1 && *expdir == '/') {
7080 cp = "/";
7081 } else {
7082 cp = expdir;
7083 enddir[-1] = '\0';
7084 }
7085 dirp = opendir(cp);
7086 if (dirp == NULL)
7087 return;
7088 if (enddir != expdir)
7089 enddir[-1] = '/';
7090 if (*endname == 0) {
7091 atend = 1;
7092 } else {
7093 atend = 0;
7094 *endname++ = '\0';
7095 }
7096 matchdot = 0;
7097 p = start;
7098 if (*p == '\\')
7099 p++;
7100 if (*p == '.')
7101 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007102 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007103 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007104 continue;
7105 if (pmatch(start, dp->d_name)) {
7106 if (atend) {
7107 strcpy(enddir, dp->d_name);
7108 addfname(expdir);
7109 } else {
7110 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7111 continue;
7112 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007113 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007114 }
7115 }
7116 }
7117 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007118 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007119 endname[-1] = '/';
7120}
7121
7122static struct strlist *
7123msort(struct strlist *list, int len)
7124{
7125 struct strlist *p, *q = NULL;
7126 struct strlist **lpp;
7127 int half;
7128 int n;
7129
7130 if (len <= 1)
7131 return list;
7132 half = len >> 1;
7133 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007134 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007135 q = p;
7136 p = p->next;
7137 }
7138 q->next = NULL; /* terminate first half of list */
7139 q = msort(list, half); /* sort first half of list */
7140 p = msort(p, len - half); /* sort second half */
7141 lpp = &list;
7142 for (;;) {
7143#if ENABLE_LOCALE_SUPPORT
7144 if (strcoll(p->text, q->text) < 0)
7145#else
7146 if (strcmp(p->text, q->text) < 0)
7147#endif
7148 {
7149 *lpp = p;
7150 lpp = &p->next;
7151 p = *lpp;
7152 if (p == NULL) {
7153 *lpp = q;
7154 break;
7155 }
7156 } else {
7157 *lpp = q;
7158 lpp = &q->next;
7159 q = *lpp;
7160 if (q == NULL) {
7161 *lpp = p;
7162 break;
7163 }
7164 }
7165 }
7166 return list;
7167}
7168
7169/*
7170 * Sort the results of file name expansion. It calculates the number of
7171 * strings to sort and then calls msort (short for merge sort) to do the
7172 * work.
7173 */
7174static struct strlist *
7175expsort(struct strlist *str)
7176{
7177 int len;
7178 struct strlist *sp;
7179
7180 len = 0;
7181 for (sp = str; sp; sp = sp->next)
7182 len++;
7183 return msort(str, len);
7184}
7185
7186static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007187expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007188{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007189 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007190 '*', '?', '[', 0
7191 };
7192 /* TODO - EXP_REDIR */
7193
7194 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007195 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007196 struct strlist **savelastp;
7197 struct strlist *sp;
7198 char *p;
7199
7200 if (fflag)
7201 goto nometa;
7202 if (!strpbrk(str->text, metachars))
7203 goto nometa;
7204 savelastp = exparg.lastp;
7205
7206 INT_OFF;
7207 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7208 {
7209 int i = strlen(str->text);
7210 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7211 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007212 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007213 free(expdir);
7214 if (p != str->text)
7215 free(p);
7216 INT_ON;
7217 if (exparg.lastp == savelastp) {
7218 /*
7219 * no matches
7220 */
7221 nometa:
7222 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007223 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007224 exparg.lastp = &str->next;
7225 } else {
7226 *exparg.lastp = NULL;
7227 *savelastp = sp = expsort(*savelastp);
7228 while (sp->next != NULL)
7229 sp = sp->next;
7230 exparg.lastp = &sp->next;
7231 }
7232 str = str->next;
7233 }
7234}
7235
7236/*
7237 * Perform variable substitution and command substitution on an argument,
7238 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7239 * perform splitting and file name expansion. When arglist is NULL, perform
7240 * here document expansion.
7241 */
7242static void
7243expandarg(union node *arg, struct arglist *arglist, int flag)
7244{
7245 struct strlist *sp;
7246 char *p;
7247
7248 argbackq = arg->narg.backquote;
7249 STARTSTACKSTR(expdest);
7250 ifsfirst.next = NULL;
7251 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007252 argstr(arg->narg.text, flag,
7253 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007254 p = _STPUTC('\0', expdest);
7255 expdest = p - 1;
7256 if (arglist == NULL) {
7257 return; /* here document expanded */
7258 }
7259 p = grabstackstr(p);
7260 exparg.lastp = &exparg.list;
7261 /*
7262 * TODO - EXP_REDIR
7263 */
7264 if (flag & EXP_FULL) {
7265 ifsbreakup(p, &exparg);
7266 *exparg.lastp = NULL;
7267 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007268 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007269 } else {
7270 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007271 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007272 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007273 sp->text = p;
7274 *exparg.lastp = sp;
7275 exparg.lastp = &sp->next;
7276 }
7277 if (ifsfirst.next)
7278 ifsfree();
7279 *exparg.lastp = NULL;
7280 if (exparg.list) {
7281 *arglist->lastp = exparg.list;
7282 arglist->lastp = exparg.lastp;
7283 }
7284}
7285
7286/*
7287 * Expand shell variables and backquotes inside a here document.
7288 */
7289static void
7290expandhere(union node *arg, int fd)
7291{
7292 herefd = fd;
7293 expandarg(arg, (struct arglist *)NULL, 0);
7294 full_write(fd, stackblock(), expdest - (char *)stackblock());
7295}
7296
7297/*
7298 * Returns true if the pattern matches the string.
7299 */
7300static int
7301patmatch(char *pattern, const char *string)
7302{
7303 return pmatch(preglob(pattern, 0, 0), string);
7304}
7305
7306/*
7307 * See if a pattern matches in a case statement.
7308 */
7309static int
7310casematch(union node *pattern, char *val)
7311{
7312 struct stackmark smark;
7313 int result;
7314
7315 setstackmark(&smark);
7316 argbackq = pattern->narg.backquote;
7317 STARTSTACKSTR(expdest);
7318 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007319 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7320 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007321 STACKSTRNUL(expdest);
7322 result = patmatch(stackblock(), val);
7323 popstackmark(&smark);
7324 return result;
7325}
7326
7327
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007328/* ============ find_command */
7329
7330struct builtincmd {
7331 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007332 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333 /* unsigned flags; */
7334};
7335#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007336/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007337 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007338#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007339#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007340
7341struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007342 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007343 union param {
7344 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007345 /* index >= 0 for commands without path (slashes) */
7346 /* (TODO: what exactly does the value mean? PATH position?) */
7347 /* index == -1 for commands with slashes */
7348 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007349 const struct builtincmd *cmd;
7350 struct funcnode *func;
7351 } u;
7352};
7353/* values of cmdtype */
7354#define CMDUNKNOWN -1 /* no entry in table for command */
7355#define CMDNORMAL 0 /* command is an executable program */
7356#define CMDFUNCTION 1 /* command is a shell function */
7357#define CMDBUILTIN 2 /* command is a shell builtin */
7358
7359/* action to find_command() */
7360#define DO_ERR 0x01 /* prints errors */
7361#define DO_ABS 0x02 /* checks absolute paths */
7362#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7363#define DO_ALTPATH 0x08 /* using alternate path */
7364#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7365
7366static void find_command(char *, struct cmdentry *, int, const char *);
7367
7368
7369/* ============ Hashing commands */
7370
7371/*
7372 * When commands are first encountered, they are entered in a hash table.
7373 * This ensures that a full path search will not have to be done for them
7374 * on each invocation.
7375 *
7376 * We should investigate converting to a linear search, even though that
7377 * would make the command name "hash" a misnomer.
7378 */
7379
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007380struct tblentry {
7381 struct tblentry *next; /* next entry in hash chain */
7382 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007383 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007384 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007385 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007386};
7387
Denis Vlasenko01631112007-12-16 17:20:38 +00007388static struct tblentry **cmdtable;
7389#define INIT_G_cmdtable() do { \
7390 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7391} while (0)
7392
7393static int builtinloc = -1; /* index in path of %builtin, or -1 */
7394
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007395
7396static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007397tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007398{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007399#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007400 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007401 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007402 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007403 while (*envp)
7404 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007405 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007406 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007407 /* re-exec ourselves with the new arguments */
7408 execve(bb_busybox_exec_path, argv, envp);
7409 /* If they called chroot or otherwise made the binary no longer
7410 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007411 }
7412#endif
7413
7414 repeat:
7415#ifdef SYSV
7416 do {
7417 execve(cmd, argv, envp);
7418 } while (errno == EINTR);
7419#else
7420 execve(cmd, argv, envp);
7421#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007422 if (cmd == (char*) bb_busybox_exec_path) {
7423 /* We already visited ENOEXEC branch below, don't do it again */
7424//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007425 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007426 return;
7427 }
7428 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007429 /* Run "cmd" as a shell script:
7430 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7431 * "If the execve() function fails with ENOEXEC, the shell
7432 * shall execute a command equivalent to having a shell invoked
7433 * with the command name as its first operand,
7434 * with any remaining arguments passed to the new shell"
7435 *
7436 * That is, do not use $SHELL, user's shell, or /bin/sh;
7437 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01007438 *
7439 * Note that bash reads ~80 chars of the file, and if it sees
7440 * a zero byte before it sees newline, it doesn't try to
7441 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01007442 * message and exit code 126. For one, this prevents attempts
7443 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007444 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007445 char **ap;
7446 char **new;
7447
7448 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007449 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007450 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7451 new[0] = (char*) "ash";
7452 new[1] = cmd;
7453 ap = new + 2;
7454 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007455 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007456 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007457 argv = new;
7458 goto repeat;
7459 }
7460}
7461
7462/*
7463 * Exec a program. Never returns. If you change this routine, you may
7464 * have to change the find_command routine as well.
7465 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007466static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007467static void
7468shellexec(char **argv, const char *path, int idx)
7469{
7470 char *cmdname;
7471 int e;
7472 char **envp;
7473 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007474 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007475
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007476 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007477 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007478 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007479#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007480 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007481#endif
7482 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007483 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007484 if (applet_no >= 0) {
7485 /* We tried execing ourself, but it didn't work.
7486 * Maybe /proc/self/exe doesn't exist?
7487 * Try $PATH search.
7488 */
7489 goto try_PATH;
7490 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007491 e = errno;
7492 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007493 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007494 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007495 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007496 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007497 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007498 if (errno != ENOENT && errno != ENOTDIR)
7499 e = errno;
7500 }
7501 stunalloc(cmdname);
7502 }
7503 }
7504
7505 /* Map to POSIX errors */
7506 switch (e) {
7507 case EACCES:
7508 exerrno = 126;
7509 break;
7510 case ENOENT:
7511 exerrno = 127;
7512 break;
7513 default:
7514 exerrno = 2;
7515 break;
7516 }
7517 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007518 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7519 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007520 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7521 /* NOTREACHED */
7522}
7523
7524static void
7525printentry(struct tblentry *cmdp)
7526{
7527 int idx;
7528 const char *path;
7529 char *name;
7530
7531 idx = cmdp->param.index;
7532 path = pathval();
7533 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007534 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007535 stunalloc(name);
7536 } while (--idx >= 0);
7537 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7538}
7539
7540/*
7541 * Clear out command entries. The argument specifies the first entry in
7542 * PATH which has changed.
7543 */
7544static void
7545clearcmdentry(int firstchange)
7546{
7547 struct tblentry **tblp;
7548 struct tblentry **pp;
7549 struct tblentry *cmdp;
7550
7551 INT_OFF;
7552 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7553 pp = tblp;
7554 while ((cmdp = *pp) != NULL) {
7555 if ((cmdp->cmdtype == CMDNORMAL &&
7556 cmdp->param.index >= firstchange)
7557 || (cmdp->cmdtype == CMDBUILTIN &&
7558 builtinloc >= firstchange)
7559 ) {
7560 *pp = cmdp->next;
7561 free(cmdp);
7562 } else {
7563 pp = &cmdp->next;
7564 }
7565 }
7566 }
7567 INT_ON;
7568}
7569
7570/*
7571 * Locate a command in the command hash table. If "add" is nonzero,
7572 * add the command to the table if it is not already present. The
7573 * variable "lastcmdentry" is set to point to the address of the link
7574 * pointing to the entry, so that delete_cmd_entry can delete the
7575 * entry.
7576 *
7577 * Interrupts must be off if called with add != 0.
7578 */
7579static struct tblentry **lastcmdentry;
7580
7581static struct tblentry *
7582cmdlookup(const char *name, int add)
7583{
7584 unsigned int hashval;
7585 const char *p;
7586 struct tblentry *cmdp;
7587 struct tblentry **pp;
7588
7589 p = name;
7590 hashval = (unsigned char)*p << 4;
7591 while (*p)
7592 hashval += (unsigned char)*p++;
7593 hashval &= 0x7FFF;
7594 pp = &cmdtable[hashval % CMDTABLESIZE];
7595 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7596 if (strcmp(cmdp->cmdname, name) == 0)
7597 break;
7598 pp = &cmdp->next;
7599 }
7600 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007601 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7602 + strlen(name)
7603 /* + 1 - already done because
7604 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007605 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007606 cmdp->cmdtype = CMDUNKNOWN;
7607 strcpy(cmdp->cmdname, name);
7608 }
7609 lastcmdentry = pp;
7610 return cmdp;
7611}
7612
7613/*
7614 * Delete the command entry returned on the last lookup.
7615 */
7616static void
7617delete_cmd_entry(void)
7618{
7619 struct tblentry *cmdp;
7620
7621 INT_OFF;
7622 cmdp = *lastcmdentry;
7623 *lastcmdentry = cmdp->next;
7624 if (cmdp->cmdtype == CMDFUNCTION)
7625 freefunc(cmdp->param.func);
7626 free(cmdp);
7627 INT_ON;
7628}
7629
7630/*
7631 * Add a new command entry, replacing any existing command entry for
7632 * the same name - except special builtins.
7633 */
7634static void
7635addcmdentry(char *name, struct cmdentry *entry)
7636{
7637 struct tblentry *cmdp;
7638
7639 cmdp = cmdlookup(name, 1);
7640 if (cmdp->cmdtype == CMDFUNCTION) {
7641 freefunc(cmdp->param.func);
7642 }
7643 cmdp->cmdtype = entry->cmdtype;
7644 cmdp->param = entry->u;
7645 cmdp->rehash = 0;
7646}
7647
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007648static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007649hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007650{
7651 struct tblentry **pp;
7652 struct tblentry *cmdp;
7653 int c;
7654 struct cmdentry entry;
7655 char *name;
7656
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007657 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007658 clearcmdentry(0);
7659 return 0;
7660 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007661
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662 if (*argptr == NULL) {
7663 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7664 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7665 if (cmdp->cmdtype == CMDNORMAL)
7666 printentry(cmdp);
7667 }
7668 }
7669 return 0;
7670 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007671
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007672 c = 0;
7673 while ((name = *argptr) != NULL) {
7674 cmdp = cmdlookup(name, 0);
7675 if (cmdp != NULL
7676 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007677 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7678 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007679 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007680 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007681 find_command(name, &entry, DO_ERR, pathval());
7682 if (entry.cmdtype == CMDUNKNOWN)
7683 c = 1;
7684 argptr++;
7685 }
7686 return c;
7687}
7688
7689/*
7690 * Called when a cd is done. Marks all commands so the next time they
7691 * are executed they will be rehashed.
7692 */
7693static void
7694hashcd(void)
7695{
7696 struct tblentry **pp;
7697 struct tblentry *cmdp;
7698
7699 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7700 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007701 if (cmdp->cmdtype == CMDNORMAL
7702 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007703 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007704 && builtinloc > 0)
7705 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007706 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007707 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007708 }
7709 }
7710}
7711
7712/*
7713 * Fix command hash table when PATH changed.
7714 * Called before PATH is changed. The argument is the new value of PATH;
7715 * pathval() still returns the old value at this point.
7716 * Called with interrupts off.
7717 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007718static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007719changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007720{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007721 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007722 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007723 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007724 int idx_bltin;
7725
7726 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007727 firstchange = 9999; /* assume no change */
7728 idx = 0;
7729 idx_bltin = -1;
7730 for (;;) {
7731 if (*old != *new) {
7732 firstchange = idx;
7733 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007734 || (*old == ':' && *new == '\0')
7735 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007736 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007737 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007738 old = new; /* ignore subsequent differences */
7739 }
7740 if (*new == '\0')
7741 break;
7742 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7743 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007744 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007745 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007746 new++;
7747 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007748 }
7749 if (builtinloc < 0 && idx_bltin >= 0)
7750 builtinloc = idx_bltin; /* zap builtins */
7751 if (builtinloc >= 0 && idx_bltin < 0)
7752 firstchange = 0;
7753 clearcmdentry(firstchange);
7754 builtinloc = idx_bltin;
7755}
7756
7757#define TEOF 0
7758#define TNL 1
7759#define TREDIR 2
7760#define TWORD 3
7761#define TSEMI 4
7762#define TBACKGND 5
7763#define TAND 6
7764#define TOR 7
7765#define TPIPE 8
7766#define TLP 9
7767#define TRP 10
7768#define TENDCASE 11
7769#define TENDBQUOTE 12
7770#define TNOT 13
7771#define TCASE 14
7772#define TDO 15
7773#define TDONE 16
7774#define TELIF 17
7775#define TELSE 18
7776#define TESAC 19
7777#define TFI 20
7778#define TFOR 21
7779#define TIF 22
7780#define TIN 23
7781#define TTHEN 24
7782#define TUNTIL 25
7783#define TWHILE 26
7784#define TBEGIN 27
7785#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007786typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007787
7788/* first char is indicating which tokens mark the end of a list */
7789static const char *const tokname_array[] = {
7790 "\1end of file",
7791 "\0newline",
7792 "\0redirection",
7793 "\0word",
7794 "\0;",
7795 "\0&",
7796 "\0&&",
7797 "\0||",
7798 "\0|",
7799 "\0(",
7800 "\1)",
7801 "\1;;",
7802 "\1`",
7803#define KWDOFFSET 13
7804 /* the following are keywords */
7805 "\0!",
7806 "\0case",
7807 "\1do",
7808 "\1done",
7809 "\1elif",
7810 "\1else",
7811 "\1esac",
7812 "\1fi",
7813 "\0for",
7814 "\0if",
7815 "\0in",
7816 "\1then",
7817 "\0until",
7818 "\0while",
7819 "\0{",
7820 "\1}",
7821};
7822
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007823/* Wrapper around strcmp for qsort/bsearch/... */
7824static int
7825pstrcmp(const void *a, const void *b)
7826{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007827 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007828}
7829
7830static const char *const *
7831findkwd(const char *s)
7832{
7833 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007834 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7835 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007836}
7837
7838/*
7839 * Locate and print what a word is...
7840 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007841static int
7842describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007843{
7844 struct cmdentry entry;
7845 struct tblentry *cmdp;
7846#if ENABLE_ASH_ALIAS
7847 const struct alias *ap;
7848#endif
7849 const char *path = pathval();
7850
7851 if (describe_command_verbose) {
7852 out1str(command);
7853 }
7854
7855 /* First look at the keywords */
7856 if (findkwd(command)) {
7857 out1str(describe_command_verbose ? " is a shell keyword" : command);
7858 goto out;
7859 }
7860
7861#if ENABLE_ASH_ALIAS
7862 /* Then look at the aliases */
7863 ap = lookupalias(command, 0);
7864 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007865 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007866 out1str("alias ");
7867 printalias(ap);
7868 return 0;
7869 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007870 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007871 goto out;
7872 }
7873#endif
7874 /* Then check if it is a tracked alias */
7875 cmdp = cmdlookup(command, 0);
7876 if (cmdp != NULL) {
7877 entry.cmdtype = cmdp->cmdtype;
7878 entry.u = cmdp->param;
7879 } else {
7880 /* Finally use brute force */
7881 find_command(command, &entry, DO_ABS, path);
7882 }
7883
7884 switch (entry.cmdtype) {
7885 case CMDNORMAL: {
7886 int j = entry.u.index;
7887 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007888 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007889 p = command;
7890 } else {
7891 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007892 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007893 stunalloc(p);
7894 } while (--j >= 0);
7895 }
7896 if (describe_command_verbose) {
7897 out1fmt(" is%s %s",
7898 (cmdp ? " a tracked alias for" : nullstr), p
7899 );
7900 } else {
7901 out1str(p);
7902 }
7903 break;
7904 }
7905
7906 case CMDFUNCTION:
7907 if (describe_command_verbose) {
7908 out1str(" is a shell function");
7909 } else {
7910 out1str(command);
7911 }
7912 break;
7913
7914 case CMDBUILTIN:
7915 if (describe_command_verbose) {
7916 out1fmt(" is a %sshell builtin",
7917 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7918 "special " : nullstr
7919 );
7920 } else {
7921 out1str(command);
7922 }
7923 break;
7924
7925 default:
7926 if (describe_command_verbose) {
7927 out1str(": not found\n");
7928 }
7929 return 127;
7930 }
7931 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007932 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007933 return 0;
7934}
7935
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007936static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007937typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007938{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007939 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007940 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007941 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007942
Denis Vlasenko46846e22007-05-20 13:08:31 +00007943 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007944 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007945 i++;
7946 verbose = 0;
7947 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007948 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007949 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007950 }
7951 return err;
7952}
7953
7954#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007955static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007956commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007957{
7958 int c;
7959 enum {
7960 VERIFY_BRIEF = 1,
7961 VERIFY_VERBOSE = 2,
7962 } verify = 0;
7963
7964 while ((c = nextopt("pvV")) != '\0')
7965 if (c == 'V')
7966 verify |= VERIFY_VERBOSE;
7967 else if (c == 'v')
7968 verify |= VERIFY_BRIEF;
7969#if DEBUG
7970 else if (c != 'p')
7971 abort();
7972#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007973 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7974 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007975 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007976 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007977
7978 return 0;
7979}
7980#endif
7981
7982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007983/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007984
Denis Vlasenko340299a2008-11-21 10:36:36 +00007985static int funcblocksize; /* size of structures in function */
7986static int funcstringsize; /* size of strings in node */
7987static void *funcblock; /* block to allocate function from */
7988static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007989
Eric Andersencb57d552001-06-28 07:25:16 +00007990/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007991#define EV_EXIT 01 /* exit after evaluating tree */
7992#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007993#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007994
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007995static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007996 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7997 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7998 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7999 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8000 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8001 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8002 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8003 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8004 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8005 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8006 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8007 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8008 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8009 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8010 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8011 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8012 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008013#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008014 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008015#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008016 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8017 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8018 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8019 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8020 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8021 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8022 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8023 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8024 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008025};
8026
8027static void calcsize(union node *n);
8028
8029static void
8030sizenodelist(struct nodelist *lp)
8031{
8032 while (lp) {
8033 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8034 calcsize(lp->n);
8035 lp = lp->next;
8036 }
8037}
8038
8039static void
8040calcsize(union node *n)
8041{
8042 if (n == NULL)
8043 return;
8044 funcblocksize += nodesize[n->type];
8045 switch (n->type) {
8046 case NCMD:
8047 calcsize(n->ncmd.redirect);
8048 calcsize(n->ncmd.args);
8049 calcsize(n->ncmd.assign);
8050 break;
8051 case NPIPE:
8052 sizenodelist(n->npipe.cmdlist);
8053 break;
8054 case NREDIR:
8055 case NBACKGND:
8056 case NSUBSHELL:
8057 calcsize(n->nredir.redirect);
8058 calcsize(n->nredir.n);
8059 break;
8060 case NAND:
8061 case NOR:
8062 case NSEMI:
8063 case NWHILE:
8064 case NUNTIL:
8065 calcsize(n->nbinary.ch2);
8066 calcsize(n->nbinary.ch1);
8067 break;
8068 case NIF:
8069 calcsize(n->nif.elsepart);
8070 calcsize(n->nif.ifpart);
8071 calcsize(n->nif.test);
8072 break;
8073 case NFOR:
8074 funcstringsize += strlen(n->nfor.var) + 1;
8075 calcsize(n->nfor.body);
8076 calcsize(n->nfor.args);
8077 break;
8078 case NCASE:
8079 calcsize(n->ncase.cases);
8080 calcsize(n->ncase.expr);
8081 break;
8082 case NCLIST:
8083 calcsize(n->nclist.body);
8084 calcsize(n->nclist.pattern);
8085 calcsize(n->nclist.next);
8086 break;
8087 case NDEFUN:
8088 case NARG:
8089 sizenodelist(n->narg.backquote);
8090 funcstringsize += strlen(n->narg.text) + 1;
8091 calcsize(n->narg.next);
8092 break;
8093 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008094#if ENABLE_ASH_BASH_COMPAT
8095 case NTO2:
8096#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008097 case NCLOBBER:
8098 case NFROM:
8099 case NFROMTO:
8100 case NAPPEND:
8101 calcsize(n->nfile.fname);
8102 calcsize(n->nfile.next);
8103 break;
8104 case NTOFD:
8105 case NFROMFD:
8106 calcsize(n->ndup.vname);
8107 calcsize(n->ndup.next);
8108 break;
8109 case NHERE:
8110 case NXHERE:
8111 calcsize(n->nhere.doc);
8112 calcsize(n->nhere.next);
8113 break;
8114 case NNOT:
8115 calcsize(n->nnot.com);
8116 break;
8117 };
8118}
8119
8120static char *
8121nodeckstrdup(char *s)
8122{
8123 char *rtn = funcstring;
8124
8125 strcpy(funcstring, s);
8126 funcstring += strlen(s) + 1;
8127 return rtn;
8128}
8129
8130static union node *copynode(union node *);
8131
8132static struct nodelist *
8133copynodelist(struct nodelist *lp)
8134{
8135 struct nodelist *start;
8136 struct nodelist **lpp;
8137
8138 lpp = &start;
8139 while (lp) {
8140 *lpp = funcblock;
8141 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8142 (*lpp)->n = copynode(lp->n);
8143 lp = lp->next;
8144 lpp = &(*lpp)->next;
8145 }
8146 *lpp = NULL;
8147 return start;
8148}
8149
8150static union node *
8151copynode(union node *n)
8152{
8153 union node *new;
8154
8155 if (n == NULL)
8156 return NULL;
8157 new = funcblock;
8158 funcblock = (char *) funcblock + nodesize[n->type];
8159
8160 switch (n->type) {
8161 case NCMD:
8162 new->ncmd.redirect = copynode(n->ncmd.redirect);
8163 new->ncmd.args = copynode(n->ncmd.args);
8164 new->ncmd.assign = copynode(n->ncmd.assign);
8165 break;
8166 case NPIPE:
8167 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008168 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008169 break;
8170 case NREDIR:
8171 case NBACKGND:
8172 case NSUBSHELL:
8173 new->nredir.redirect = copynode(n->nredir.redirect);
8174 new->nredir.n = copynode(n->nredir.n);
8175 break;
8176 case NAND:
8177 case NOR:
8178 case NSEMI:
8179 case NWHILE:
8180 case NUNTIL:
8181 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8182 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8183 break;
8184 case NIF:
8185 new->nif.elsepart = copynode(n->nif.elsepart);
8186 new->nif.ifpart = copynode(n->nif.ifpart);
8187 new->nif.test = copynode(n->nif.test);
8188 break;
8189 case NFOR:
8190 new->nfor.var = nodeckstrdup(n->nfor.var);
8191 new->nfor.body = copynode(n->nfor.body);
8192 new->nfor.args = copynode(n->nfor.args);
8193 break;
8194 case NCASE:
8195 new->ncase.cases = copynode(n->ncase.cases);
8196 new->ncase.expr = copynode(n->ncase.expr);
8197 break;
8198 case NCLIST:
8199 new->nclist.body = copynode(n->nclist.body);
8200 new->nclist.pattern = copynode(n->nclist.pattern);
8201 new->nclist.next = copynode(n->nclist.next);
8202 break;
8203 case NDEFUN:
8204 case NARG:
8205 new->narg.backquote = copynodelist(n->narg.backquote);
8206 new->narg.text = nodeckstrdup(n->narg.text);
8207 new->narg.next = copynode(n->narg.next);
8208 break;
8209 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008210#if ENABLE_ASH_BASH_COMPAT
8211 case NTO2:
8212#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008213 case NCLOBBER:
8214 case NFROM:
8215 case NFROMTO:
8216 case NAPPEND:
8217 new->nfile.fname = copynode(n->nfile.fname);
8218 new->nfile.fd = n->nfile.fd;
8219 new->nfile.next = copynode(n->nfile.next);
8220 break;
8221 case NTOFD:
8222 case NFROMFD:
8223 new->ndup.vname = copynode(n->ndup.vname);
8224 new->ndup.dupfd = n->ndup.dupfd;
8225 new->ndup.fd = n->ndup.fd;
8226 new->ndup.next = copynode(n->ndup.next);
8227 break;
8228 case NHERE:
8229 case NXHERE:
8230 new->nhere.doc = copynode(n->nhere.doc);
8231 new->nhere.fd = n->nhere.fd;
8232 new->nhere.next = copynode(n->nhere.next);
8233 break;
8234 case NNOT:
8235 new->nnot.com = copynode(n->nnot.com);
8236 break;
8237 };
8238 new->type = n->type;
8239 return new;
8240}
8241
8242/*
8243 * Make a copy of a parse tree.
8244 */
8245static struct funcnode *
8246copyfunc(union node *n)
8247{
8248 struct funcnode *f;
8249 size_t blocksize;
8250
8251 funcblocksize = offsetof(struct funcnode, n);
8252 funcstringsize = 0;
8253 calcsize(n);
8254 blocksize = funcblocksize;
8255 f = ckmalloc(blocksize + funcstringsize);
8256 funcblock = (char *) f + offsetof(struct funcnode, n);
8257 funcstring = (char *) f + blocksize;
8258 copynode(n);
8259 f->count = 0;
8260 return f;
8261}
8262
8263/*
8264 * Define a shell function.
8265 */
8266static void
8267defun(char *name, union node *func)
8268{
8269 struct cmdentry entry;
8270
8271 INT_OFF;
8272 entry.cmdtype = CMDFUNCTION;
8273 entry.u.func = copyfunc(func);
8274 addcmdentry(name, &entry);
8275 INT_ON;
8276}
8277
Denis Vlasenko4b875702009-03-19 13:30:04 +00008278/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008279#define SKIPBREAK (1 << 0)
8280#define SKIPCONT (1 << 1)
8281#define SKIPFUNC (1 << 2)
8282#define SKIPFILE (1 << 3)
8283#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008284static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008285static int skipcount; /* number of levels to skip */
8286static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008287static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008288
Denis Vlasenko4b875702009-03-19 13:30:04 +00008289/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008290static int evalstring(char *s, int mask);
8291
Denis Vlasenko4b875702009-03-19 13:30:04 +00008292/* Called to execute a trap.
8293 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008294 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008295 *
8296 * Perhaps we should avoid entering new trap handlers
8297 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008298 */
8299static int
8300dotrap(void)
8301{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008302 uint8_t *g;
8303 int sig;
8304 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008305
8306 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008307 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008308 xbarrier();
8309
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008310 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008311 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8312 int want_exexit;
8313 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008314
Denis Vlasenko4b875702009-03-19 13:30:04 +00008315 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008316 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008317 t = trap[sig];
8318 /* non-trapped SIGINT is handled separately by raise_interrupt,
8319 * don't upset it by resetting gotsig[SIGINT-1] */
8320 if (sig == SIGINT && !t)
8321 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008322
8323 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008324 *g = 0;
8325 if (!t)
8326 continue;
8327 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008328 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008329 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008330 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008331 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008332 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008333 }
8334
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008335 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008336 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008337}
8338
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008339/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008340static void evalloop(union node *, int);
8341static void evalfor(union node *, int);
8342static void evalcase(union node *, int);
8343static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008344static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008345static void evalpipe(union node *, int);
8346static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008347static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008348static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008349
Eric Andersen62483552001-07-10 06:09:16 +00008350/*
Eric Andersenc470f442003-07-28 09:56:35 +00008351 * Evaluate a parse tree. The value is left in the global variable
8352 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008353 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008354static void
Eric Andersenc470f442003-07-28 09:56:35 +00008355evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008356{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008357 struct jmploc *volatile savehandler = exception_handler;
8358 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008359 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008360 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008361 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008362 int int_level;
8363
8364 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008365
Eric Andersenc470f442003-07-28 09:56:35 +00008366 if (n == NULL) {
8367 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008368 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008369 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008370 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008371
8372 exception_handler = &jmploc;
8373 {
8374 int err = setjmp(jmploc.loc);
8375 if (err) {
8376 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008377 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008378 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8379 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008380 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008381 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008382 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008383 TRACE(("exception %d in evaltree, propagating err=%d\n",
8384 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008385 exception_handler = savehandler;
8386 longjmp(exception_handler->loc, err);
8387 }
8388 }
8389
Eric Andersenc470f442003-07-28 09:56:35 +00008390 switch (n->type) {
8391 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008392#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008393 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008394 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008395 break;
8396#endif
8397 case NNOT:
8398 evaltree(n->nnot.com, EV_TESTED);
8399 status = !exitstatus;
8400 goto setstatus;
8401 case NREDIR:
8402 expredir(n->nredir.redirect);
8403 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8404 if (!status) {
8405 evaltree(n->nredir.n, flags & EV_TESTED);
8406 status = exitstatus;
8407 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008408 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008409 goto setstatus;
8410 case NCMD:
8411 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008412 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008413 if (eflag && !(flags & EV_TESTED))
8414 checkexit = ~0;
8415 goto calleval;
8416 case NFOR:
8417 evalfn = evalfor;
8418 goto calleval;
8419 case NWHILE:
8420 case NUNTIL:
8421 evalfn = evalloop;
8422 goto calleval;
8423 case NSUBSHELL:
8424 case NBACKGND:
8425 evalfn = evalsubshell;
8426 goto calleval;
8427 case NPIPE:
8428 evalfn = evalpipe;
8429 goto checkexit;
8430 case NCASE:
8431 evalfn = evalcase;
8432 goto calleval;
8433 case NAND:
8434 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008435 case NSEMI: {
8436
Eric Andersenc470f442003-07-28 09:56:35 +00008437#if NAND + 1 != NOR
8438#error NAND + 1 != NOR
8439#endif
8440#if NOR + 1 != NSEMI
8441#error NOR + 1 != NSEMI
8442#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008443 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008444 evaltree(
8445 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008446 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008447 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008448 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008449 break;
8450 if (!evalskip) {
8451 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008452 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008453 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008454 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008455 evalfn(n, flags);
8456 break;
8457 }
8458 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008459 }
Eric Andersenc470f442003-07-28 09:56:35 +00008460 case NIF:
8461 evaltree(n->nif.test, EV_TESTED);
8462 if (evalskip)
8463 break;
8464 if (exitstatus == 0) {
8465 n = n->nif.ifpart;
8466 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008467 }
8468 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008469 n = n->nif.elsepart;
8470 goto evaln;
8471 }
8472 goto success;
8473 case NDEFUN:
8474 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008475 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008476 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008477 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008478 exitstatus = status;
8479 break;
8480 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008481
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008482 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008483 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008484
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008485 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008486 /* Order of checks below is important:
8487 * signal handlers trigger before exit caused by "set -e".
8488 */
8489 if (pending_sig && dotrap())
8490 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008491 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008492 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008493
8494 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008495 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008496 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008497 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008498
8499 RESTORE_INT(int_level);
8500 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008501}
8502
Eric Andersenc470f442003-07-28 09:56:35 +00008503#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8504static
8505#endif
8506void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8507
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008508static void
Eric Andersenc470f442003-07-28 09:56:35 +00008509evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008510{
8511 int status;
8512
8513 loopnest++;
8514 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008515 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008516 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008517 int i;
8518
Eric Andersencb57d552001-06-28 07:25:16 +00008519 evaltree(n->nbinary.ch1, EV_TESTED);
8520 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008521 skipping:
8522 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008523 evalskip = 0;
8524 continue;
8525 }
8526 if (evalskip == SKIPBREAK && --skipcount <= 0)
8527 evalskip = 0;
8528 break;
8529 }
Eric Andersenc470f442003-07-28 09:56:35 +00008530 i = exitstatus;
8531 if (n->type != NWHILE)
8532 i = !i;
8533 if (i != 0)
8534 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008535 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008536 status = exitstatus;
8537 if (evalskip)
8538 goto skipping;
8539 }
8540 loopnest--;
8541 exitstatus = status;
8542}
8543
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008544static void
Eric Andersenc470f442003-07-28 09:56:35 +00008545evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008546{
8547 struct arglist arglist;
8548 union node *argp;
8549 struct strlist *sp;
8550 struct stackmark smark;
8551
8552 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008553 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008554 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008555 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008556 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008557 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008558 if (evalskip)
8559 goto out;
8560 }
8561 *arglist.lastp = NULL;
8562
8563 exitstatus = 0;
8564 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008565 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008566 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008567 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008568 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008569 if (evalskip) {
8570 if (evalskip == SKIPCONT && --skipcount <= 0) {
8571 evalskip = 0;
8572 continue;
8573 }
8574 if (evalskip == SKIPBREAK && --skipcount <= 0)
8575 evalskip = 0;
8576 break;
8577 }
8578 }
8579 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008580 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008581 popstackmark(&smark);
8582}
8583
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008584static void
Eric Andersenc470f442003-07-28 09:56:35 +00008585evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008586{
8587 union node *cp;
8588 union node *patp;
8589 struct arglist arglist;
8590 struct stackmark smark;
8591
8592 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008593 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008594 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008595 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008596 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008597 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8598 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008599 if (casematch(patp, arglist.list->text)) {
8600 if (evalskip == 0) {
8601 evaltree(cp->nclist.body, flags);
8602 }
8603 goto out;
8604 }
8605 }
8606 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008607 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008608 popstackmark(&smark);
8609}
8610
Eric Andersenc470f442003-07-28 09:56:35 +00008611/*
8612 * Kick off a subshell to evaluate a tree.
8613 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008614static void
Eric Andersenc470f442003-07-28 09:56:35 +00008615evalsubshell(union node *n, int flags)
8616{
8617 struct job *jp;
8618 int backgnd = (n->type == NBACKGND);
8619 int status;
8620
8621 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008622 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008623 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008624 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008625 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008626 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008627 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008628 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008629 flags |= EV_EXIT;
8630 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008631 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008632 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008633 redirect(n->nredir.redirect, 0);
8634 evaltreenr(n->nredir.n, flags);
8635 /* never returns */
8636 }
8637 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008638 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008639 status = waitforjob(jp);
8640 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008641 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008642}
8643
Eric Andersenc470f442003-07-28 09:56:35 +00008644/*
8645 * Compute the names of the files in a redirection list.
8646 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008647static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008648static void
8649expredir(union node *n)
8650{
8651 union node *redir;
8652
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008653 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008654 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008655
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008656 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008657 fn.lastp = &fn.list;
8658 switch (redir->type) {
8659 case NFROMTO:
8660 case NFROM:
8661 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008662#if ENABLE_ASH_BASH_COMPAT
8663 case NTO2:
8664#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008665 case NCLOBBER:
8666 case NAPPEND:
8667 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008668#if ENABLE_ASH_BASH_COMPAT
8669 store_expfname:
8670#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008671 redir->nfile.expfname = fn.list->text;
8672 break;
8673 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008674 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008675 if (redir->ndup.vname) {
8676 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008677 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008678 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008679#if ENABLE_ASH_BASH_COMPAT
8680//FIXME: we used expandarg with different args!
8681 if (!isdigit_str9(fn.list->text)) {
8682 /* >&file, not >&fd */
8683 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8684 ash_msg_and_raise_error("redir error");
8685 redir->type = NTO2;
8686 goto store_expfname;
8687 }
8688#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008689 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008690 }
8691 break;
8692 }
8693 }
8694}
8695
Eric Andersencb57d552001-06-28 07:25:16 +00008696/*
Eric Andersencb57d552001-06-28 07:25:16 +00008697 * Evaluate a pipeline. All the processes in the pipeline are children
8698 * of the process creating the pipeline. (This differs from some versions
8699 * of the shell, which make the last process in a pipeline the parent
8700 * of all the rest.)
8701 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008702static void
Eric Andersenc470f442003-07-28 09:56:35 +00008703evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008704{
8705 struct job *jp;
8706 struct nodelist *lp;
8707 int pipelen;
8708 int prevfd;
8709 int pip[2];
8710
Eric Andersenc470f442003-07-28 09:56:35 +00008711 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008712 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008713 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008714 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008715 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008716 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008717 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008718 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008719 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008720 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008721 pip[1] = -1;
8722 if (lp->next) {
8723 if (pipe(pip) < 0) {
8724 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008725 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008726 }
8727 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008728 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008729 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008730 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008731 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008732 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008733 if (prevfd > 0) {
8734 dup2(prevfd, 0);
8735 close(prevfd);
8736 }
8737 if (pip[1] > 1) {
8738 dup2(pip[1], 1);
8739 close(pip[1]);
8740 }
Eric Andersenc470f442003-07-28 09:56:35 +00008741 evaltreenr(lp->n, flags);
8742 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008743 }
8744 if (prevfd >= 0)
8745 close(prevfd);
8746 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008747 /* Don't want to trigger debugging */
8748 if (pip[1] != -1)
8749 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008750 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008751 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008752 exitstatus = waitforjob(jp);
8753 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008754 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008755 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008756}
8757
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008758/*
8759 * Controls whether the shell is interactive or not.
8760 */
8761static void
8762setinteractive(int on)
8763{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008764 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008765
8766 if (++on == is_interactive)
8767 return;
8768 is_interactive = on;
8769 setsignal(SIGINT);
8770 setsignal(SIGQUIT);
8771 setsignal(SIGTERM);
8772#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8773 if (is_interactive > 1) {
8774 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008775 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008776
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008777 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008778 /* note: ash and hush share this string */
8779 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008780 "Enter 'help' for a list of built-in commands."
8781 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008782 bb_banner,
8783 "built-in shell (ash)"
8784 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008785 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008786 }
8787 }
8788#endif
8789}
8790
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008791static void
8792optschanged(void)
8793{
8794#if DEBUG
8795 opentrace();
8796#endif
8797 setinteractive(iflag);
8798 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008799#if ENABLE_FEATURE_EDITING_VI
8800 if (viflag)
8801 line_input_state->flags |= VI_MODE;
8802 else
8803 line_input_state->flags &= ~VI_MODE;
8804#else
8805 viflag = 0; /* forcibly keep the option off */
8806#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008807}
8808
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008809static struct localvar *localvars;
8810
8811/*
8812 * Called after a function returns.
8813 * Interrupts must be off.
8814 */
8815static void
8816poplocalvars(void)
8817{
8818 struct localvar *lvp;
8819 struct var *vp;
8820
8821 while ((lvp = localvars) != NULL) {
8822 localvars = lvp->next;
8823 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008824 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008825 if (vp == NULL) { /* $- saved */
8826 memcpy(optlist, lvp->text, sizeof(optlist));
8827 free((char*)lvp->text);
8828 optschanged();
8829 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008830 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008831 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008832 if (vp->var_func)
8833 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008834 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008835 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008836 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008837 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008838 }
8839 free(lvp);
8840 }
8841}
8842
8843static int
8844evalfun(struct funcnode *func, int argc, char **argv, int flags)
8845{
8846 volatile struct shparam saveparam;
8847 struct localvar *volatile savelocalvars;
8848 struct jmploc *volatile savehandler;
8849 struct jmploc jmploc;
8850 int e;
8851
8852 saveparam = shellparam;
8853 savelocalvars = localvars;
8854 e = setjmp(jmploc.loc);
8855 if (e) {
8856 goto funcdone;
8857 }
8858 INT_OFF;
8859 savehandler = exception_handler;
8860 exception_handler = &jmploc;
8861 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008862 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008863 func->count++;
8864 funcnest++;
8865 INT_ON;
8866 shellparam.nparam = argc - 1;
8867 shellparam.p = argv + 1;
8868#if ENABLE_ASH_GETOPTS
8869 shellparam.optind = 1;
8870 shellparam.optoff = -1;
8871#endif
8872 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008873 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008874 INT_OFF;
8875 funcnest--;
8876 freefunc(func);
8877 poplocalvars();
8878 localvars = savelocalvars;
8879 freeparam(&shellparam);
8880 shellparam = saveparam;
8881 exception_handler = savehandler;
8882 INT_ON;
8883 evalskip &= ~SKIPFUNC;
8884 return e;
8885}
8886
Denis Vlasenko131ae172007-02-18 13:00:19 +00008887#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008888static char **
8889parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008890{
8891 char *cp, c;
8892
8893 for (;;) {
8894 cp = *++argv;
8895 if (!cp)
8896 return 0;
8897 if (*cp++ != '-')
8898 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008899 c = *cp++;
8900 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008901 break;
8902 if (c == '-' && !*cp) {
8903 argv++;
8904 break;
8905 }
8906 do {
8907 switch (c) {
8908 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008909 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008910 break;
8911 default:
8912 /* run 'typecmd' for other options */
8913 return 0;
8914 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008915 c = *cp++;
8916 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008917 }
8918 return argv;
8919}
8920#endif
8921
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008922/*
8923 * Make a variable a local variable. When a variable is made local, it's
8924 * value and flags are saved in a localvar structure. The saved values
8925 * will be restored when the shell function returns. We handle the name
8926 * "-" as a special case.
8927 */
8928static void
8929mklocal(char *name)
8930{
8931 struct localvar *lvp;
8932 struct var **vpp;
8933 struct var *vp;
8934
8935 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008936 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008937 if (LONE_DASH(name)) {
8938 char *p;
8939 p = ckmalloc(sizeof(optlist));
8940 lvp->text = memcpy(p, optlist, sizeof(optlist));
8941 vp = NULL;
8942 } else {
8943 char *eq;
8944
8945 vpp = hashvar(name);
8946 vp = *findvar(vpp, name);
8947 eq = strchr(name, '=');
8948 if (vp == NULL) {
8949 if (eq)
8950 setvareq(name, VSTRFIXED);
8951 else
8952 setvar(name, NULL, VSTRFIXED);
8953 vp = *vpp; /* the new variable */
8954 lvp->flags = VUNSET;
8955 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008956 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008957 lvp->flags = vp->flags;
8958 vp->flags |= VSTRFIXED|VTEXTFIXED;
8959 if (eq)
8960 setvareq(name, 0);
8961 }
8962 }
8963 lvp->vp = vp;
8964 lvp->next = localvars;
8965 localvars = lvp;
8966 INT_ON;
8967}
8968
8969/*
8970 * The "local" command.
8971 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008972static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008973localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008974{
8975 char *name;
8976
8977 argv = argptr;
8978 while ((name = *argv++) != NULL) {
8979 mklocal(name);
8980 }
8981 return 0;
8982}
8983
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008984static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008985falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008986{
8987 return 1;
8988}
8989
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008990static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008991truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008992{
8993 return 0;
8994}
8995
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008996static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008997execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008998{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008999 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009000 iflag = 0; /* exit on error */
9001 mflag = 0;
9002 optschanged();
9003 shellexec(argv + 1, pathval(), 0);
9004 }
9005 return 0;
9006}
9007
9008/*
9009 * The return command.
9010 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009011static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009012returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009013{
9014 /*
9015 * If called outside a function, do what ksh does;
9016 * skip the rest of the file.
9017 */
9018 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9019 return argv[1] ? number(argv[1]) : exitstatus;
9020}
9021
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009022/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009023static int breakcmd(int, char **) FAST_FUNC;
9024static int dotcmd(int, char **) FAST_FUNC;
9025static int evalcmd(int, char **) FAST_FUNC;
9026static int exitcmd(int, char **) FAST_FUNC;
9027static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009028#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009029static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009030#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009031#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009032static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009033#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009034#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009035static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009036#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009037static int readcmd(int, char **) FAST_FUNC;
9038static int setcmd(int, char **) FAST_FUNC;
9039static int shiftcmd(int, char **) FAST_FUNC;
9040static int timescmd(int, char **) FAST_FUNC;
9041static int trapcmd(int, char **) FAST_FUNC;
9042static int umaskcmd(int, char **) FAST_FUNC;
9043static int unsetcmd(int, char **) FAST_FUNC;
9044static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009045
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009046#define BUILTIN_NOSPEC "0"
9047#define BUILTIN_SPECIAL "1"
9048#define BUILTIN_REGULAR "2"
9049#define BUILTIN_SPEC_REG "3"
9050#define BUILTIN_ASSIGN "4"
9051#define BUILTIN_SPEC_ASSG "5"
9052#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009053#define BUILTIN_SPEC_REG_ASSG "7"
9054
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009055/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009056#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009057static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009058#endif
9059#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009060static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009061#endif
9062#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009063static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009064#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009065
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009066/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009067static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009068 { BUILTIN_SPEC_REG "." , dotcmd },
9069 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009072#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009073 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009074#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009075#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009076#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009077 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009078#endif
9079#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009080 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009081#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009082 { BUILTIN_SPEC_REG "break" , breakcmd },
9083 { BUILTIN_REGULAR "cd" , cdcmd },
9084 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009085#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009086 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009087#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009088 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009089#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009090 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009091#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009092 { BUILTIN_SPEC_REG "eval" , evalcmd },
9093 { BUILTIN_SPEC_REG "exec" , execcmd },
9094 { BUILTIN_SPEC_REG "exit" , exitcmd },
9095 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9096 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009097#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009098 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009099#endif
9100#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009101 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009102#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009103 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009104#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009105 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009106#endif
9107#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009108 { BUILTIN_REGULAR "jobs" , jobscmd },
9109 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009110#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009111#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009112 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009113#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009114 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009115#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009116 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009117#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009118 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9119 { BUILTIN_REGULAR "read" , readcmd },
9120 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9121 { BUILTIN_SPEC_REG "return" , returncmd },
9122 { BUILTIN_SPEC_REG "set" , setcmd },
9123 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009124#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009125 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009126#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009127#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009128 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009129#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009130 { BUILTIN_SPEC_REG "times" , timescmd },
9131 { BUILTIN_SPEC_REG "trap" , trapcmd },
9132 { BUILTIN_REGULAR "true" , truecmd },
9133 { BUILTIN_NOSPEC "type" , typecmd },
9134 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9135 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009136#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009137 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009138#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009139 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9140 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009141};
9142
Denis Vlasenko80591b02008-03-25 07:49:43 +00009143/* Should match the above table! */
9144#define COMMANDCMD (builtintab + \
9145 2 + \
9146 1 * ENABLE_ASH_BUILTIN_TEST + \
9147 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9148 1 * ENABLE_ASH_ALIAS + \
9149 1 * ENABLE_ASH_JOB_CONTROL + \
9150 3)
9151#define EXECCMD (builtintab + \
9152 2 + \
9153 1 * ENABLE_ASH_BUILTIN_TEST + \
9154 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9155 1 * ENABLE_ASH_ALIAS + \
9156 1 * ENABLE_ASH_JOB_CONTROL + \
9157 3 + \
9158 1 * ENABLE_ASH_CMDCMD + \
9159 1 + \
9160 ENABLE_ASH_BUILTIN_ECHO + \
9161 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009162
9163/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009164 * Search the table of builtin commands.
9165 */
9166static struct builtincmd *
9167find_builtin(const char *name)
9168{
9169 struct builtincmd *bp;
9170
9171 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009172 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009173 pstrcmp
9174 );
9175 return bp;
9176}
9177
9178/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009179 * Execute a simple command.
9180 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009181static int
9182isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009183{
9184 const char *q = endofname(p);
9185 if (p == q)
9186 return 0;
9187 return *q == '=';
9188}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009189static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009190bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009191{
9192 /* Preserve exitstatus of a previous possible redirection
9193 * as POSIX mandates */
9194 return back_exitstatus;
9195}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009196static void
Eric Andersenc470f442003-07-28 09:56:35 +00009197evalcommand(union node *cmd, int flags)
9198{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009199 static const struct builtincmd null_bltin = {
9200 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009201 };
Eric Andersenc470f442003-07-28 09:56:35 +00009202 struct stackmark smark;
9203 union node *argp;
9204 struct arglist arglist;
9205 struct arglist varlist;
9206 char **argv;
9207 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009208 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009209 struct cmdentry cmdentry;
9210 struct job *jp;
9211 char *lastarg;
9212 const char *path;
9213 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009214 int status;
9215 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009216 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009217 smallint cmd_is_exec;
9218 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009219
9220 /* First expand the arguments. */
9221 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9222 setstackmark(&smark);
9223 back_exitstatus = 0;
9224
9225 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009226 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009227 varlist.lastp = &varlist.list;
9228 *varlist.lastp = NULL;
9229 arglist.lastp = &arglist.list;
9230 *arglist.lastp = NULL;
9231
9232 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009233 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009234 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9235 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9236 }
9237
Eric Andersenc470f442003-07-28 09:56:35 +00009238 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9239 struct strlist **spp;
9240
9241 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009242 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009243 expandarg(argp, &arglist, EXP_VARTILDE);
9244 else
9245 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9246
Eric Andersenc470f442003-07-28 09:56:35 +00009247 for (sp = *spp; sp; sp = sp->next)
9248 argc++;
9249 }
9250
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009251 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009252 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009253 TRACE(("evalcommand arg: %s\n", sp->text));
9254 *nargv++ = sp->text;
9255 }
9256 *nargv = NULL;
9257
9258 lastarg = NULL;
9259 if (iflag && funcnest == 0 && argc > 0)
9260 lastarg = nargv[-1];
9261
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009262 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009263 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009264 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009265
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009266 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009267 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9268 struct strlist **spp;
9269 char *p;
9270
9271 spp = varlist.lastp;
9272 expandarg(argp, &varlist, EXP_VARTILDE);
9273
9274 /*
9275 * Modify the command lookup path, if a PATH= assignment
9276 * is present
9277 */
9278 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009279 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009280 path = p;
9281 }
9282
9283 /* Print the command if xflag is set. */
9284 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009285 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009286 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009287
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009288 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009289 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009290 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009291 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009292 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009293 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009294 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009295 }
9296 sp = arglist.list;
9297 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009298 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009299 }
9300
9301 cmd_is_exec = 0;
9302 spclbltin = -1;
9303
9304 /* Now locate the command. */
9305 if (argc) {
9306 const char *oldpath;
9307 int cmd_flag = DO_ERR;
9308
9309 path += 5;
9310 oldpath = path;
9311 for (;;) {
9312 find_command(argv[0], &cmdentry, cmd_flag, path);
9313 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009314 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009315 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009316 goto bail;
9317 }
9318
9319 /* implement bltin and command here */
9320 if (cmdentry.cmdtype != CMDBUILTIN)
9321 break;
9322 if (spclbltin < 0)
9323 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9324 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009325 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009326#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009327 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009328 path = oldpath;
9329 nargv = parse_command_args(argv, &path);
9330 if (!nargv)
9331 break;
9332 argc -= nargv - argv;
9333 argv = nargv;
9334 cmd_flag |= DO_NOFUNC;
9335 } else
9336#endif
9337 break;
9338 }
9339 }
9340
9341 if (status) {
9342 /* We have a redirection error. */
9343 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009344 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009345 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009346 exitstatus = status;
9347 goto out;
9348 }
9349
9350 /* Execute the command. */
9351 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009352 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009353
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009354#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009355/* (1) BUG: if variables are set, we need to fork, or save/restore them
9356 * around run_nofork_applet() call.
9357 * (2) Should this check also be done in forkshell()?
9358 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9359 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009360 /* find_command() encodes applet_no as (-2 - applet_no) */
9361 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009362 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009363 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009364 /* run <applet>_main() */
9365 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009366 break;
9367 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009368#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009369 /* Can we avoid forking off? For example, very last command
9370 * in a script or a subshell does not need forking,
9371 * we can just exec it.
9372 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009373 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009374 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009375 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009376 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009377 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009378 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009379 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009380 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009381 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009382 break;
9383 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009384 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009385 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009386 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009387 }
9388 listsetvar(varlist.list, VEXPORT|VSTACK);
9389 shellexec(argv, path, cmdentry.u.index);
9390 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009391 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009392 case CMDBUILTIN:
9393 cmdenviron = varlist.list;
9394 if (cmdenviron) {
9395 struct strlist *list = cmdenviron;
9396 int i = VNOSET;
9397 if (spclbltin > 0 || argc == 0) {
9398 i = 0;
9399 if (cmd_is_exec && argc > 1)
9400 i = VEXPORT;
9401 }
9402 listsetvar(list, i);
9403 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009404 /* Tight loop with builtins only:
9405 * "while kill -0 $child; do true; done"
9406 * will never exit even if $child died, unless we do this
9407 * to reap the zombie and make kill detect that it's gone: */
9408 dowait(DOWAIT_NONBLOCK, NULL);
9409
Eric Andersenc470f442003-07-28 09:56:35 +00009410 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9411 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009412 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009413 if (i == EXEXIT)
9414 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009415 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009416 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009417 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009418 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009419 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009420 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009421 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009422 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009423 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009424 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009425 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009426 }
9427 break;
9428
9429 case CMDFUNCTION:
9430 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009431 /* See above for the rationale */
9432 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009433 if (evalfun(cmdentry.u.func, argc, argv, flags))
9434 goto raise;
9435 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009436
9437 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009438
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009439 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009440 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009441 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009442 /* dsl: I think this is intended to be used to support
9443 * '_' in 'vi' command mode during line editing...
9444 * However I implemented that within libedit itself.
9445 */
9446 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009447 }
Eric Andersenc470f442003-07-28 09:56:35 +00009448 popstackmark(&smark);
9449}
9450
9451static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009452evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9453{
Eric Andersenc470f442003-07-28 09:56:35 +00009454 char *volatile savecmdname;
9455 struct jmploc *volatile savehandler;
9456 struct jmploc jmploc;
9457 int i;
9458
9459 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009460 i = setjmp(jmploc.loc);
9461 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009462 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009463 savehandler = exception_handler;
9464 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009465 commandname = argv[0];
9466 argptr = argv + 1;
9467 optptr = NULL; /* initialize nextopt */
9468 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009469 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009470 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009471 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009472 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009473 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009474 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009475
9476 return i;
9477}
9478
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009479static int
9480goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009481{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009482 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009483}
9484
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009485
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009486/*
9487 * Search for a command. This is called before we fork so that the
9488 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009489 * the child. The check for "goodname" is an overly conservative
9490 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009491 */
Eric Andersenc470f442003-07-28 09:56:35 +00009492static void
9493prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009494{
9495 struct cmdentry entry;
9496
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009497 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9498 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009499}
9500
Eric Andersencb57d552001-06-28 07:25:16 +00009501
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009502/* ============ Builtin commands
9503 *
9504 * Builtin commands whose functions are closely tied to evaluation
9505 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009506 */
9507
9508/*
Eric Andersencb57d552001-06-28 07:25:16 +00009509 * Handle break and continue commands. Break, continue, and return are
9510 * all handled by setting the evalskip flag. The evaluation routines
9511 * above all check this flag, and if it is set they start skipping
9512 * commands rather than executing them. The variable skipcount is
9513 * the number of loops to break/continue, or the number of function
9514 * levels to return. (The latter is always 1.) It should probably
9515 * be an error to break out of more loops than exist, but it isn't
9516 * in the standard shell so we don't make it one here.
9517 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009518static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009519breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009520{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009521 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009522
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009523 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009524 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009525 if (n > loopnest)
9526 n = loopnest;
9527 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009528 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009529 skipcount = n;
9530 }
9531 return 0;
9532}
9533
Eric Andersenc470f442003-07-28 09:56:35 +00009534
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009535/* ============ input.c
9536 *
Eric Andersen90898442003-08-06 11:20:52 +00009537 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009538 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009539
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009540enum {
9541 INPUT_PUSH_FILE = 1,
9542 INPUT_NOFILE_OK = 2,
9543};
Eric Andersencb57d552001-06-28 07:25:16 +00009544
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009545static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009546/* values of checkkwd variable */
9547#define CHKALIAS 0x1
9548#define CHKKWD 0x2
9549#define CHKNL 0x4
9550
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009551/*
9552 * Push a string back onto the input at this current parsefile level.
9553 * We handle aliases this way.
9554 */
9555#if !ENABLE_ASH_ALIAS
9556#define pushstring(s, ap) pushstring(s)
9557#endif
9558static void
9559pushstring(char *s, struct alias *ap)
9560{
9561 struct strpush *sp;
9562 int len;
9563
9564 len = strlen(s);
9565 INT_OFF;
9566 if (g_parsefile->strpush) {
9567 sp = ckzalloc(sizeof(*sp));
9568 sp->prev = g_parsefile->strpush;
9569 } else {
9570 sp = &(g_parsefile->basestrpush);
9571 }
9572 g_parsefile->strpush = sp;
9573 sp->prev_string = g_parsefile->next_to_pgetc;
9574 sp->prev_left_in_line = g_parsefile->left_in_line;
9575#if ENABLE_ASH_ALIAS
9576 sp->ap = ap;
9577 if (ap) {
9578 ap->flag |= ALIASINUSE;
9579 sp->string = s;
9580 }
9581#endif
9582 g_parsefile->next_to_pgetc = s;
9583 g_parsefile->left_in_line = len;
9584 INT_ON;
9585}
9586
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009587static void
9588popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009589{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009590 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009591
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009592 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009593#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009594 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009595 if (g_parsefile->next_to_pgetc[-1] == ' '
9596 || g_parsefile->next_to_pgetc[-1] == '\t'
9597 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009598 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009599 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009600 if (sp->string != sp->ap->val) {
9601 free(sp->string);
9602 }
9603 sp->ap->flag &= ~ALIASINUSE;
9604 if (sp->ap->flag & ALIASDEAD) {
9605 unalias(sp->ap->name);
9606 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009607 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009608#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009609 g_parsefile->next_to_pgetc = sp->prev_string;
9610 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009611 g_parsefile->strpush = sp->prev;
9612 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009613 free(sp);
9614 INT_ON;
9615}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009616
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009617//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9618//it peeks whether it is &>, and then pushes back both chars.
9619//This function needs to save last *next_to_pgetc to buf[0]
9620//to make two pungetc() reliable. Currently,
9621// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009622static int
9623preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009624{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009625 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009626 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009627
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009628 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009629#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009630 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009631 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009632 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009633 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009634 int timeout = -1;
9635# if ENABLE_ASH_IDLE_TIMEOUT
9636 if (iflag) {
9637 const char *tmout_var = lookupvar("TMOUT");
9638 if (tmout_var) {
9639 timeout = atoi(tmout_var) * 1000;
9640 if (timeout <= 0)
9641 timeout = -1;
9642 }
9643 }
9644# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009645# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009646 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009647# endif
Denys Vlasenko20704f02011-03-23 17:59:27 +01009648 /* Unicode support should be activated even if LANG is set
9649 * _during_ shell execution, not only if it was set when
9650 * shell was started. Therefore, re-check LANG every time:
9651 */
9652 reinit_unicode(lookupvar("LANG"));
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009653 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009654 if (nr == 0) {
9655 /* Ctrl+C pressed */
9656 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009657 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009658 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009659 raise(SIGINT);
9660 return 1;
9661 }
Eric Andersenc470f442003-07-28 09:56:35 +00009662 goto retry;
9663 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009664 if (nr < 0) {
9665 if (errno == 0) {
9666 /* Ctrl+D pressed */
9667 nr = 0;
9668 }
9669# if ENABLE_ASH_IDLE_TIMEOUT
9670 else if (errno == EAGAIN && timeout > 0) {
9671 printf("\007timed out waiting for input: auto-logout\n");
9672 exitshell();
9673 }
9674# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009675 }
Eric Andersencb57d552001-06-28 07:25:16 +00009676 }
9677#else
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009678 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009679#endif
9680
Denys Vlasenko80c5b682011-05-08 21:21:10 +02009681#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009682 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009683 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009684 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009685 if (flags >= 0 && (flags & O_NONBLOCK)) {
9686 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009687 if (fcntl(0, F_SETFL, flags) >= 0) {
9688 out2str("sh: turning off NDELAY mode\n");
9689 goto retry;
9690 }
9691 }
9692 }
9693 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009694#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009695 return nr;
9696}
9697
9698/*
9699 * Refill the input buffer and return the next input character:
9700 *
9701 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009702 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9703 * or we are reading from a string so we can't refill the buffer,
9704 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009705 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009706 * 4) Process input up to the next newline, deleting nul characters.
9707 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009708//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9709#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009710static int
Eric Andersenc470f442003-07-28 09:56:35 +00009711preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009712{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009713 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009714 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009715
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009716 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009717#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009718 if (g_parsefile->left_in_line == -1
9719 && g_parsefile->strpush->ap
9720 && g_parsefile->next_to_pgetc[-1] != ' '
9721 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009722 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009723 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009724 return PEOA;
9725 }
Eric Andersen2870d962001-07-02 17:27:21 +00009726#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009727 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009728 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009729 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9730 g_parsefile->left_in_line,
9731 g_parsefile->next_to_pgetc,
9732 g_parsefile->next_to_pgetc);
9733 if (--g_parsefile->left_in_line >= 0)
9734 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009735 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009736 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009737 * "pgetc" needs refilling.
9738 */
9739
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009740 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009741 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009742 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009743 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009744 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009745 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009746 /* even in failure keep left_in_line and next_to_pgetc
9747 * in lock step, for correct multi-layer pungetc.
9748 * left_in_line was decremented before preadbuffer(),
9749 * must inc next_to_pgetc: */
9750 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009751 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009752 }
Eric Andersencb57d552001-06-28 07:25:16 +00009753
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009754 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009755 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009756 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009757 again:
9758 more = preadfd();
9759 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009760 /* don't try reading again */
9761 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009762 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009763 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009764 return PEOF;
9765 }
9766 }
9767
Denis Vlasenko727752d2008-11-28 03:41:47 +00009768 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009769 * Set g_parsefile->left_in_line
9770 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009771 * NUL chars are deleted.
9772 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009773 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009774 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009775 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009776
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009777 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009778
Denis Vlasenko727752d2008-11-28 03:41:47 +00009779 c = *q;
9780 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009781 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009782 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009783 q++;
9784 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009785 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009786 break;
9787 }
Eric Andersencb57d552001-06-28 07:25:16 +00009788 }
9789
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009790 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009791 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9792 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009793 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009794 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009795 }
9796 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009797 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009798
Eric Andersencb57d552001-06-28 07:25:16 +00009799 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009800 char save = *q;
9801 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009802 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009803 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009804 }
9805
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009806 pgetc_debug("preadbuffer at %d:%p'%s'",
9807 g_parsefile->left_in_line,
9808 g_parsefile->next_to_pgetc,
9809 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009810 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009811}
9812
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009813#define pgetc_as_macro() \
9814 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009815 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009816 : preadbuffer() \
9817 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009818
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009819static int
9820pgetc(void)
9821{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009822 pgetc_debug("pgetc_fast at %d:%p'%s'",
9823 g_parsefile->left_in_line,
9824 g_parsefile->next_to_pgetc,
9825 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009826 return pgetc_as_macro();
9827}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009828
9829#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009830# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009831#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009832# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009833#endif
9834
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009835#if ENABLE_ASH_ALIAS
9836static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009837pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009838{
9839 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009840 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009841 pgetc_debug("pgetc_fast at %d:%p'%s'",
9842 g_parsefile->left_in_line,
9843 g_parsefile->next_to_pgetc,
9844 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009845 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009846 } while (c == PEOA);
9847 return c;
9848}
9849#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009850# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009851#endif
9852
9853/*
9854 * Read a line from the script.
9855 */
9856static char *
9857pfgets(char *line, int len)
9858{
9859 char *p = line;
9860 int nleft = len;
9861 int c;
9862
9863 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009864 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009865 if (c == PEOF) {
9866 if (p == line)
9867 return NULL;
9868 break;
9869 }
9870 *p++ = c;
9871 if (c == '\n')
9872 break;
9873 }
9874 *p = '\0';
9875 return line;
9876}
9877
Eric Andersenc470f442003-07-28 09:56:35 +00009878/*
9879 * Undo the last call to pgetc. Only one character may be pushed back.
9880 * PEOF may be pushed back.
9881 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009882static void
Eric Andersenc470f442003-07-28 09:56:35 +00009883pungetc(void)
9884{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009885 g_parsefile->left_in_line++;
9886 g_parsefile->next_to_pgetc--;
9887 pgetc_debug("pushed back to %d:%p'%s'",
9888 g_parsefile->left_in_line,
9889 g_parsefile->next_to_pgetc,
9890 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009891}
9892
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009893/*
9894 * To handle the "." command, a stack of input files is used. Pushfile
9895 * adds a new entry to the stack and popfile restores the previous level.
9896 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009897static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009898pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009899{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009900 struct parsefile *pf;
9901
Denis Vlasenko597906c2008-02-20 16:38:54 +00009902 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009903 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009904 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009905 /*pf->strpush = NULL; - ckzalloc did it */
9906 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009907 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009908}
9909
9910static void
9911popfile(void)
9912{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009913 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009914
Denis Vlasenkob012b102007-02-19 22:43:01 +00009915 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009916 if (pf->pf_fd >= 0)
9917 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009918 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009919 while (pf->strpush)
9920 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009921 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009922 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009923 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009924}
9925
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009926/*
9927 * Return to top level.
9928 */
9929static void
9930popallfiles(void)
9931{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009932 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009933 popfile();
9934}
9935
9936/*
9937 * Close the file(s) that the shell is reading commands from. Called
9938 * after a fork is done.
9939 */
9940static void
9941closescript(void)
9942{
9943 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009944 if (g_parsefile->pf_fd > 0) {
9945 close(g_parsefile->pf_fd);
9946 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009947 }
9948}
9949
9950/*
9951 * Like setinputfile, but takes an open file descriptor. Call this with
9952 * interrupts off.
9953 */
9954static void
9955setinputfd(int fd, int push)
9956{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009957 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009958 if (push) {
9959 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009960 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009961 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009962 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009963 if (g_parsefile->buf == NULL)
9964 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009965 g_parsefile->left_in_buffer = 0;
9966 g_parsefile->left_in_line = 0;
9967 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009968}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009969
Eric Andersenc470f442003-07-28 09:56:35 +00009970/*
9971 * Set the input to take input from a file. If push is set, push the
9972 * old input onto the stack first.
9973 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009974static int
9975setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009976{
9977 int fd;
9978 int fd2;
9979
Denis Vlasenkob012b102007-02-19 22:43:01 +00009980 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009981 fd = open(fname, O_RDONLY);
9982 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009983 if (flags & INPUT_NOFILE_OK)
9984 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009985 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009986 }
Eric Andersenc470f442003-07-28 09:56:35 +00009987 if (fd < 10) {
9988 fd2 = copyfd(fd, 10);
9989 close(fd);
9990 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009991 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009992 fd = fd2;
9993 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009994 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009995 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009996 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009997 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009998}
9999
Eric Andersencb57d552001-06-28 07:25:16 +000010000/*
10001 * Like setinputfile, but takes input from a string.
10002 */
Eric Andersenc470f442003-07-28 09:56:35 +000010003static void
10004setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000010005{
Denis Vlasenkob012b102007-02-19 22:43:01 +000010006 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010007 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010008 g_parsefile->next_to_pgetc = string;
10009 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010010 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010011 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010012 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010013}
10014
10015
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010016/* ============ mail.c
10017 *
10018 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000010019 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010020
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010021#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000010022
Eric Andersencb57d552001-06-28 07:25:16 +000010023#define MAXMBOXES 10
10024
Eric Andersenc470f442003-07-28 09:56:35 +000010025/* times of mailboxes */
10026static time_t mailtime[MAXMBOXES];
10027/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010028static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010029
Eric Andersencb57d552001-06-28 07:25:16 +000010030/*
Eric Andersenc470f442003-07-28 09:56:35 +000010031 * Print appropriate message(s) if mail has arrived.
10032 * If mail_var_path_changed is set,
10033 * then the value of MAIL has mail_var_path_changed,
10034 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010035 */
Eric Andersenc470f442003-07-28 09:56:35 +000010036static void
10037chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010038{
Eric Andersencb57d552001-06-28 07:25:16 +000010039 const char *mpath;
10040 char *p;
10041 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010042 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010043 struct stackmark smark;
10044 struct stat statb;
10045
Eric Andersencb57d552001-06-28 07:25:16 +000010046 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010047 mpath = mpathset() ? mpathval() : mailval();
10048 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010049 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010050 if (p == NULL)
10051 break;
10052 if (*p == '\0')
10053 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010054 for (q = p; *q; q++)
10055 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010056#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010057 if (q[-1] != '/')
10058 abort();
10059#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010060 q[-1] = '\0'; /* delete trailing '/' */
10061 if (stat(p, &statb) < 0) {
10062 *mtp = 0;
10063 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010064 }
Eric Andersenc470f442003-07-28 09:56:35 +000010065 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10066 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010067 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010068 pathopt ? pathopt : "you have mail"
10069 );
10070 }
10071 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010072 }
Eric Andersenc470f442003-07-28 09:56:35 +000010073 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010074 popstackmark(&smark);
10075}
Eric Andersencb57d552001-06-28 07:25:16 +000010076
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010077static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010078changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010079{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010080 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010081}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010082
Denis Vlasenko131ae172007-02-18 13:00:19 +000010083#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010084
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010085
10086/* ============ ??? */
10087
Eric Andersencb57d552001-06-28 07:25:16 +000010088/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010089 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010090 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010091static void
10092setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010093{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010094 char **newparam;
10095 char **ap;
10096 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010097
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010098 for (nparam = 0; argv[nparam]; nparam++)
10099 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010100 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10101 while (*argv) {
10102 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010103 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010104 *ap = NULL;
10105 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010106 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010107 shellparam.nparam = nparam;
10108 shellparam.p = newparam;
10109#if ENABLE_ASH_GETOPTS
10110 shellparam.optind = 1;
10111 shellparam.optoff = -1;
10112#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010113}
10114
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010115/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010116 * Process shell options. The global variable argptr contains a pointer
10117 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010118 *
10119 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10120 * For a non-interactive shell, an error condition encountered
10121 * by a special built-in ... shall cause the shell to write a diagnostic message
10122 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010123 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010124 * ...
10125 * Utility syntax error (option or operand error) Shall exit
10126 * ...
10127 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10128 * we see that bash does not do that (set "finishes" with error code 1 instead,
10129 * and shell continues), and people rely on this behavior!
10130 * Testcase:
10131 * set -o barfoo 2>/dev/null
10132 * echo $?
10133 *
10134 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010135 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010136static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010137plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010138{
10139 int i;
10140
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010141 if (name) {
10142 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010143 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010144 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010145 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010146 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010147 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010148 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010149 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010150 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010151 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010152 if (val) {
10153 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10154 } else {
10155 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10156 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010157 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010158 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010159}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010160static void
10161setoption(int flag, int val)
10162{
10163 int i;
10164
10165 for (i = 0; i < NOPTS; i++) {
10166 if (optletters(i) == flag) {
10167 optlist[i] = val;
10168 return;
10169 }
10170 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010171 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010172 /* NOTREACHED */
10173}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010174static int
Eric Andersenc470f442003-07-28 09:56:35 +000010175options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010176{
10177 char *p;
10178 int val;
10179 int c;
10180
10181 if (cmdline)
10182 minusc = NULL;
10183 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010184 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010185 if (c != '-' && c != '+')
10186 break;
10187 argptr++;
10188 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010189 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010190 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010191 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010192 if (!cmdline) {
10193 /* "-" means turn off -x and -v */
10194 if (p[0] == '\0')
10195 xflag = vflag = 0;
10196 /* "--" means reset params */
10197 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010198 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010199 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010200 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010201 }
Eric Andersencb57d552001-06-28 07:25:16 +000010202 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010203 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010204 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010205 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010206 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010207 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010208 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010209 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010210 /* it already printed err message */
10211 return 1; /* error */
10212 }
Eric Andersencb57d552001-06-28 07:25:16 +000010213 if (*argptr)
10214 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010215 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10216 isloginsh = 1;
10217 /* bash does not accept +-login, we also won't */
10218 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010219 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010220 isloginsh = 1;
10221 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010222 } else {
10223 setoption(c, val);
10224 }
10225 }
10226 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010227 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010228}
10229
Eric Andersencb57d552001-06-28 07:25:16 +000010230/*
Eric Andersencb57d552001-06-28 07:25:16 +000010231 * The shift builtin command.
10232 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010233static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010234shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010235{
10236 int n;
10237 char **ap1, **ap2;
10238
10239 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010240 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010241 n = number(argv[1]);
10242 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010243 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010244 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010245 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010246 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010247 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010248 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010249 }
10250 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010251 while ((*ap2++ = *ap1++) != NULL)
10252 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010253#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010254 shellparam.optind = 1;
10255 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010256#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010257 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010258 return 0;
10259}
10260
Eric Andersencb57d552001-06-28 07:25:16 +000010261/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010262 * POSIX requires that 'set' (but not export or readonly) output the
10263 * variables in lexicographic order - by the locale's collating order (sigh).
10264 * Maybe we could keep them in an ordered balanced binary tree
10265 * instead of hashed lists.
10266 * For now just roll 'em through qsort for printing...
10267 */
10268static int
10269showvars(const char *sep_prefix, int on, int off)
10270{
10271 const char *sep;
10272 char **ep, **epend;
10273
10274 ep = listvars(on, off, &epend);
10275 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10276
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010277 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010278
10279 for (; ep < epend; ep++) {
10280 const char *p;
10281 const char *q;
10282
10283 p = strchrnul(*ep, '=');
10284 q = nullstr;
10285 if (*p)
10286 q = single_quote(++p);
10287 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10288 }
10289 return 0;
10290}
10291
10292/*
Eric Andersencb57d552001-06-28 07:25:16 +000010293 * The set command builtin.
10294 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010295static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010296setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010297{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010298 int retval;
10299
Denis Vlasenko68404f12008-03-17 09:00:54 +000010300 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010301 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010302
Denis Vlasenkob012b102007-02-19 22:43:01 +000010303 INT_OFF;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010304 retval = options(/*cmdline:*/ 0);
10305 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010306 optschanged();
10307 if (*argptr != NULL) {
10308 setparam(argptr);
10309 }
Eric Andersencb57d552001-06-28 07:25:16 +000010310 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010311 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010312 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010313}
10314
Denis Vlasenko131ae172007-02-18 13:00:19 +000010315#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010316static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010317change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010318{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010319 uint32_t t;
10320
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010321 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010322 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010323 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010324 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010325 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010326 vrandom.flags &= ~VNOFUNC;
10327 } else {
10328 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010329 t = strtoul(value, NULL, 10);
10330 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010331 }
Eric Andersenef02f822004-03-11 13:34:24 +000010332}
Eric Andersen16767e22004-03-16 05:14:10 +000010333#endif
10334
Denis Vlasenko131ae172007-02-18 13:00:19 +000010335#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010336static int
Eric Andersenc470f442003-07-28 09:56:35 +000010337getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010338{
10339 char *p, *q;
10340 char c = '?';
10341 int done = 0;
10342 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010343 char s[12];
10344 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010345
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010346 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010347 return 1;
10348 optnext = optfirst + *param_optind - 1;
10349
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010350 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010351 p = NULL;
10352 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010353 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010354 if (p == NULL || *p == '\0') {
10355 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010356 p = *optnext;
10357 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010358 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010359 p = NULL;
10360 done = 1;
10361 goto out;
10362 }
10363 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010364 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010365 goto atend;
10366 }
10367
10368 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010369 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010370 if (*q == '\0') {
10371 if (optstr[0] == ':') {
10372 s[0] = c;
10373 s[1] = '\0';
10374 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010375 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010376 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010377 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010378 }
10379 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010380 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010381 }
10382 if (*++q == ':')
10383 q++;
10384 }
10385
10386 if (*++q == ':') {
10387 if (*p == '\0' && (p = *optnext) == NULL) {
10388 if (optstr[0] == ':') {
10389 s[0] = c;
10390 s[1] = '\0';
10391 err |= setvarsafe("OPTARG", s, 0);
10392 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010393 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010394 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010395 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010396 c = '?';
10397 }
Eric Andersenc470f442003-07-28 09:56:35 +000010398 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010399 }
10400
10401 if (p == *optnext)
10402 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010403 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010404 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010405 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010406 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010407 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010408 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010409 *param_optind = optnext - optfirst + 1;
10410 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010411 err |= setvarsafe("OPTIND", s, VNOFUNC);
10412 s[0] = c;
10413 s[1] = '\0';
10414 err |= setvarsafe(optvar, s, 0);
10415 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010416 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010417 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010418 flush_stdout_stderr();
10419 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010420 }
10421 return done;
10422}
Eric Andersenc470f442003-07-28 09:56:35 +000010423
10424/*
10425 * The getopts builtin. Shellparam.optnext points to the next argument
10426 * to be processed. Shellparam.optptr points to the next character to
10427 * be processed in the current argument. If shellparam.optnext is NULL,
10428 * then it's the first time getopts has been called.
10429 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010430static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010431getoptscmd(int argc, char **argv)
10432{
10433 char **optbase;
10434
10435 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010436 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010437 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010438 optbase = shellparam.p;
10439 if (shellparam.optind > shellparam.nparam + 1) {
10440 shellparam.optind = 1;
10441 shellparam.optoff = -1;
10442 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010443 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010444 optbase = &argv[3];
10445 if (shellparam.optind > argc - 2) {
10446 shellparam.optind = 1;
10447 shellparam.optoff = -1;
10448 }
10449 }
10450
10451 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010452 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010453}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010454#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010455
Eric Andersencb57d552001-06-28 07:25:16 +000010456
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010457/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010458
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010459struct heredoc {
10460 struct heredoc *next; /* next here document in list */
10461 union node *here; /* redirection node */
10462 char *eofmark; /* string indicating end of input */
10463 smallint striptabs; /* if set, strip leading tabs */
10464};
10465
10466static smallint tokpushback; /* last token pushed back */
10467static smallint parsebackquote; /* nonzero if we are inside backquotes */
10468static smallint quoteflag; /* set if (part of) last token was quoted */
10469static token_id_t lasttoken; /* last token read (integer id Txxx) */
10470static struct heredoc *heredoclist; /* list of here documents to read */
10471static char *wordtext; /* text of last word returned by readtoken */
10472static struct nodelist *backquotelist;
10473static union node *redirnode;
10474static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010475
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010476static const char *
10477tokname(char *buf, int tok)
10478{
10479 if (tok < TSEMI)
10480 return tokname_array[tok] + 1;
10481 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10482 return buf;
10483}
10484
10485/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010486 * Called when an unexpected token is read during the parse. The argument
10487 * is the token that is expected, or -1 if more than one type of token can
10488 * occur at this point.
10489 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010490static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010491static void
10492raise_error_unexpected_syntax(int token)
10493{
10494 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010495 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010496 int l;
10497
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010498 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010499 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010500 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010501 raise_error_syntax(msg);
10502 /* NOTREACHED */
10503}
Eric Andersencb57d552001-06-28 07:25:16 +000010504
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010505#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010506
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010507/* parsing is heavily cross-recursive, need these forward decls */
10508static union node *andor(void);
10509static union node *pipeline(void);
10510static union node *parse_command(void);
10511static void parseheredoc(void);
10512static char peektoken(void);
10513static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010514
Eric Andersenc470f442003-07-28 09:56:35 +000010515static union node *
10516list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010517{
10518 union node *n1, *n2, *n3;
10519 int tok;
10520
Eric Andersenc470f442003-07-28 09:56:35 +000010521 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10522 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010523 return NULL;
10524 n1 = NULL;
10525 for (;;) {
10526 n2 = andor();
10527 tok = readtoken();
10528 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010529 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010530 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010531 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010532 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010533 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010534 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010535 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010536 n2 = n3;
10537 }
10538 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010539 }
10540 }
10541 if (n1 == NULL) {
10542 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010543 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010544 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010545 n3->type = NSEMI;
10546 n3->nbinary.ch1 = n1;
10547 n3->nbinary.ch2 = n2;
10548 n1 = n3;
10549 }
10550 switch (tok) {
10551 case TBACKGND:
10552 case TSEMI:
10553 tok = readtoken();
10554 /* fall through */
10555 case TNL:
10556 if (tok == TNL) {
10557 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010558 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010559 return n1;
10560 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010561 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010562 }
Eric Andersenc470f442003-07-28 09:56:35 +000010563 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010564 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010565 return n1;
10566 break;
10567 case TEOF:
10568 if (heredoclist)
10569 parseheredoc();
10570 else
Eric Andersenc470f442003-07-28 09:56:35 +000010571 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010572 return n1;
10573 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010574 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010575 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010576 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010577 return n1;
10578 }
10579 }
10580}
10581
Eric Andersenc470f442003-07-28 09:56:35 +000010582static union node *
10583andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010584{
Eric Andersencb57d552001-06-28 07:25:16 +000010585 union node *n1, *n2, *n3;
10586 int t;
10587
Eric Andersencb57d552001-06-28 07:25:16 +000010588 n1 = pipeline();
10589 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010590 t = readtoken();
10591 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010592 t = NAND;
10593 } else if (t == TOR) {
10594 t = NOR;
10595 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010596 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010597 return n1;
10598 }
Eric Andersenc470f442003-07-28 09:56:35 +000010599 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010600 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010601 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010602 n3->type = t;
10603 n3->nbinary.ch1 = n1;
10604 n3->nbinary.ch2 = n2;
10605 n1 = n3;
10606 }
10607}
10608
Eric Andersenc470f442003-07-28 09:56:35 +000010609static union node *
10610pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010611{
Eric Andersencb57d552001-06-28 07:25:16 +000010612 union node *n1, *n2, *pipenode;
10613 struct nodelist *lp, *prev;
10614 int negate;
10615
10616 negate = 0;
10617 TRACE(("pipeline: entered\n"));
10618 if (readtoken() == TNOT) {
10619 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010620 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010621 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010622 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010623 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010624 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010625 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010626 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010627 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010628 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010629 pipenode->npipe.cmdlist = lp;
10630 lp->n = n1;
10631 do {
10632 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010633 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010634 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010635 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010636 prev->next = lp;
10637 } while (readtoken() == TPIPE);
10638 lp->next = NULL;
10639 n1 = pipenode;
10640 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010641 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010642 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010643 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010644 n2->type = NNOT;
10645 n2->nnot.com = n1;
10646 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010647 }
10648 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010649}
10650
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010651static union node *
10652makename(void)
10653{
10654 union node *n;
10655
Denis Vlasenko597906c2008-02-20 16:38:54 +000010656 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010657 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010658 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010659 n->narg.text = wordtext;
10660 n->narg.backquote = backquotelist;
10661 return n;
10662}
10663
10664static void
10665fixredir(union node *n, const char *text, int err)
10666{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010667 int fd;
10668
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010669 TRACE(("Fix redir %s %d\n", text, err));
10670 if (!err)
10671 n->ndup.vname = NULL;
10672
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010673 fd = bb_strtou(text, NULL, 10);
10674 if (!errno && fd >= 0)
10675 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010676 else if (LONE_DASH(text))
10677 n->ndup.dupfd = -1;
10678 else {
10679 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010680 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010681 n->ndup.vname = makename();
10682 }
10683}
10684
10685/*
10686 * Returns true if the text contains nothing to expand (no dollar signs
10687 * or backquotes).
10688 */
10689static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010690noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010691{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010692 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010693
Denys Vlasenkocd716832009-11-28 22:14:02 +010010694 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010695 if (c == CTLQUOTEMARK)
10696 continue;
10697 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010698 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010699 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010700 return 0;
10701 }
10702 return 1;
10703}
10704
10705static void
10706parsefname(void)
10707{
10708 union node *n = redirnode;
10709
10710 if (readtoken() != TWORD)
10711 raise_error_unexpected_syntax(-1);
10712 if (n->type == NHERE) {
10713 struct heredoc *here = heredoc;
10714 struct heredoc *p;
10715 int i;
10716
10717 if (quoteflag == 0)
10718 n->type = NXHERE;
10719 TRACE(("Here document %d\n", n->type));
10720 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010721 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010722 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010723 here->eofmark = wordtext;
10724 here->next = NULL;
10725 if (heredoclist == NULL)
10726 heredoclist = here;
10727 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010728 for (p = heredoclist; p->next; p = p->next)
10729 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010730 p->next = here;
10731 }
10732 } else if (n->type == NTOFD || n->type == NFROMFD) {
10733 fixredir(n, wordtext, 0);
10734 } else {
10735 n->nfile.fname = makename();
10736 }
10737}
Eric Andersencb57d552001-06-28 07:25:16 +000010738
Eric Andersenc470f442003-07-28 09:56:35 +000010739static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010740simplecmd(void)
10741{
10742 union node *args, **app;
10743 union node *n = NULL;
10744 union node *vars, **vpp;
10745 union node **rpp, *redir;
10746 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010747#if ENABLE_ASH_BASH_COMPAT
10748 smallint double_brackets_flag = 0;
10749#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010750
10751 args = NULL;
10752 app = &args;
10753 vars = NULL;
10754 vpp = &vars;
10755 redir = NULL;
10756 rpp = &redir;
10757
10758 savecheckkwd = CHKALIAS;
10759 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010760 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010761 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010762 t = readtoken();
10763 switch (t) {
10764#if ENABLE_ASH_BASH_COMPAT
10765 case TAND: /* "&&" */
10766 case TOR: /* "||" */
10767 if (!double_brackets_flag) {
10768 tokpushback = 1;
10769 goto out;
10770 }
10771 wordtext = (char *) (t == TAND ? "-a" : "-o");
10772#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010773 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010774 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010775 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010776 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010777 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010778#if ENABLE_ASH_BASH_COMPAT
10779 if (strcmp("[[", wordtext) == 0)
10780 double_brackets_flag = 1;
10781 else if (strcmp("]]", wordtext) == 0)
10782 double_brackets_flag = 0;
10783#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010784 n->narg.backquote = backquotelist;
10785 if (savecheckkwd && isassignment(wordtext)) {
10786 *vpp = n;
10787 vpp = &n->narg.next;
10788 } else {
10789 *app = n;
10790 app = &n->narg.next;
10791 savecheckkwd = 0;
10792 }
10793 break;
10794 case TREDIR:
10795 *rpp = n = redirnode;
10796 rpp = &n->nfile.next;
10797 parsefname(); /* read name of redirection file */
10798 break;
10799 case TLP:
10800 if (args && app == &args->narg.next
10801 && !vars && !redir
10802 ) {
10803 struct builtincmd *bcmd;
10804 const char *name;
10805
10806 /* We have a function */
10807 if (readtoken() != TRP)
10808 raise_error_unexpected_syntax(TRP);
10809 name = n->narg.text;
10810 if (!goodname(name)
10811 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10812 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010813 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010814 }
10815 n->type = NDEFUN;
10816 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10817 n->narg.next = parse_command();
10818 return n;
10819 }
10820 /* fall through */
10821 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010822 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010823 goto out;
10824 }
10825 }
10826 out:
10827 *app = NULL;
10828 *vpp = NULL;
10829 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010830 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010831 n->type = NCMD;
10832 n->ncmd.args = args;
10833 n->ncmd.assign = vars;
10834 n->ncmd.redirect = redir;
10835 return n;
10836}
10837
10838static union node *
10839parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010840{
Eric Andersencb57d552001-06-28 07:25:16 +000010841 union node *n1, *n2;
10842 union node *ap, **app;
10843 union node *cp, **cpp;
10844 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010845 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010846 int t;
10847
10848 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010849 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010850
Eric Andersencb57d552001-06-28 07:25:16 +000010851 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010852 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010853 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010854 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010855 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010856 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010857 n1->type = NIF;
10858 n1->nif.test = list(0);
10859 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010860 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010861 n1->nif.ifpart = list(0);
10862 n2 = n1;
10863 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010864 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010865 n2 = n2->nif.elsepart;
10866 n2->type = NIF;
10867 n2->nif.test = list(0);
10868 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010869 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010870 n2->nif.ifpart = list(0);
10871 }
10872 if (lasttoken == TELSE)
10873 n2->nif.elsepart = list(0);
10874 else {
10875 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010876 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010877 }
Eric Andersenc470f442003-07-28 09:56:35 +000010878 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010879 break;
10880 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010881 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010882 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010883 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010884 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010885 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010886 got = readtoken();
10887 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010888 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010889 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010890 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010891 }
10892 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010893 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010894 break;
10895 }
10896 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010897 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010898 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010899 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010900 n1->type = NFOR;
10901 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010902 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010903 if (readtoken() == TIN) {
10904 app = &ap;
10905 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010906 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010907 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010908 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010909 n2->narg.text = wordtext;
10910 n2->narg.backquote = backquotelist;
10911 *app = n2;
10912 app = &n2->narg.next;
10913 }
10914 *app = NULL;
10915 n1->nfor.args = ap;
10916 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010917 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010918 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010919 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010920 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010921 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010922 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010923 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010924 n1->nfor.args = n2;
10925 /*
10926 * Newline or semicolon here is optional (but note
10927 * that the original Bourne shell only allowed NL).
10928 */
10929 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010930 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010931 }
Eric Andersenc470f442003-07-28 09:56:35 +000010932 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010933 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010934 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010935 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010936 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010937 break;
10938 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010939 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010940 n1->type = NCASE;
10941 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010942 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010943 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010944 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010945 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010946 n2->narg.text = wordtext;
10947 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010948 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010949 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010950 } while (readtoken() == TNL);
10951 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010952 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010953 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010954 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010955 checkkwd = CHKNL | CHKKWD;
10956 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010957 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010958 if (lasttoken == TLP)
10959 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010960 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010961 cp->type = NCLIST;
10962 app = &cp->nclist.pattern;
10963 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010964 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010965 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010966 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010967 ap->narg.text = wordtext;
10968 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010969 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010970 break;
10971 app = &ap->narg.next;
10972 readtoken();
10973 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010974 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010975 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010976 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010977 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010978
Eric Andersenc470f442003-07-28 09:56:35 +000010979 cpp = &cp->nclist.next;
10980
10981 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010982 t = readtoken();
10983 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010984 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010985 raise_error_unexpected_syntax(TENDCASE);
10986 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010987 }
Eric Andersenc470f442003-07-28 09:56:35 +000010988 }
Eric Andersencb57d552001-06-28 07:25:16 +000010989 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010990 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010991 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010992 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010993 n1->type = NSUBSHELL;
10994 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010995 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010996 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010997 break;
10998 case TBEGIN:
10999 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000011000 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000011001 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011002 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000011003 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011004 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011005 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000011006 }
11007
Eric Andersenc470f442003-07-28 09:56:35 +000011008 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011009 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000011010
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011011 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000011012 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000011013 checkkwd = CHKKWD | CHKALIAS;
11014 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000011015 while (readtoken() == TREDIR) {
11016 *rpp = n2 = redirnode;
11017 rpp = &n2->nfile.next;
11018 parsefname();
11019 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011020 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011021 *rpp = NULL;
11022 if (redir) {
11023 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011024 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011025 n2->type = NREDIR;
11026 n2->nredir.n = n1;
11027 n1 = n2;
11028 }
11029 n1->nredir.redirect = redir;
11030 }
Eric Andersencb57d552001-06-28 07:25:16 +000011031 return n1;
11032}
11033
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011034#if ENABLE_ASH_BASH_COMPAT
11035static int decode_dollar_squote(void)
11036{
11037 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11038 int c, cnt;
11039 char *p;
11040 char buf[4];
11041
11042 c = pgetc();
11043 p = strchr(C_escapes, c);
11044 if (p) {
11045 buf[0] = c;
11046 p = buf;
11047 cnt = 3;
11048 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11049 do {
11050 c = pgetc();
11051 *++p = c;
11052 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11053 pungetc();
11054 } else if (c == 'x') { /* \xHH */
11055 do {
11056 c = pgetc();
11057 *++p = c;
11058 } while (isxdigit(c) && --cnt);
11059 pungetc();
11060 if (cnt == 3) { /* \x but next char is "bad" */
11061 c = 'x';
11062 goto unrecognized;
11063 }
11064 } else { /* simple seq like \\ or \t */
11065 p++;
11066 }
11067 *p = '\0';
11068 p = buf;
11069 c = bb_process_escape_sequence((void*)&p);
11070 } else { /* unrecognized "\z": print both chars unless ' or " */
11071 if (c != '\'' && c != '"') {
11072 unrecognized:
11073 c |= 0x100; /* "please encode \, then me" */
11074 }
11075 }
11076 return c;
11077}
11078#endif
11079
Eric Andersencb57d552001-06-28 07:25:16 +000011080/*
11081 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11082 * is not NULL, read a here document. In the latter case, eofmark is the
11083 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011084 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011085 * is the first character of the input token or document.
11086 *
11087 * Because C does not have internal subroutines, I have simulated them
11088 * using goto's to implement the subroutine linkage. The following macros
11089 * will run code that appears at the end of readtoken1.
11090 */
Eric Andersen2870d962001-07-02 17:27:21 +000011091#define CHECKEND() {goto checkend; checkend_return:;}
11092#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11093#define PARSESUB() {goto parsesub; parsesub_return:;}
11094#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11095#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11096#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011097static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011098readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011099{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011100 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011101 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011102 char *out;
11103 int len;
11104 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011105 struct nodelist *bqlist;
11106 smallint quotef;
11107 smallint dblquote;
11108 smallint oldstyle;
11109 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011110#if ENABLE_ASH_EXPAND_PRMT
11111 smallint pssyntax; /* we are expanding a prompt string */
11112#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011113 int varnest; /* levels of variables expansion */
11114 int arinest; /* levels of arithmetic expansion */
11115 int parenlevel; /* levels of parens in arithmetic */
11116 int dqvarnest; /* levels of variables expansion within double quotes */
11117
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011118 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011119
Eric Andersencb57d552001-06-28 07:25:16 +000011120#if __GNUC__
11121 /* Avoid longjmp clobbering */
11122 (void) &out;
11123 (void) &quotef;
11124 (void) &dblquote;
11125 (void) &varnest;
11126 (void) &arinest;
11127 (void) &parenlevel;
11128 (void) &dqvarnest;
11129 (void) &oldstyle;
11130 (void) &prevsyntax;
11131 (void) &syntax;
11132#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011133 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011134 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011135 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011136 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011137#if ENABLE_ASH_EXPAND_PRMT
11138 pssyntax = (syntax == PSSYNTAX);
11139 if (pssyntax)
11140 syntax = DQSYNTAX;
11141#endif
11142 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011143 varnest = 0;
11144 arinest = 0;
11145 parenlevel = 0;
11146 dqvarnest = 0;
11147
11148 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011149 loop:
11150 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011151 CHECKEND(); /* set c to PEOF if at end of here document */
11152 for (;;) { /* until end of line or end of word */
11153 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11154 switch (SIT(c, syntax)) {
11155 case CNL: /* '\n' */
11156 if (syntax == BASESYNTAX)
11157 goto endword; /* exit outer loop */
11158 USTPUTC(c, out);
11159 g_parsefile->linno++;
11160 setprompt_if(doprompt, 2);
11161 c = pgetc();
11162 goto loop; /* continue outer loop */
11163 case CWORD:
11164 USTPUTC(c, out);
11165 break;
11166 case CCTL:
11167 if (eofmark == NULL || dblquote)
11168 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011169#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011170 if (c == '\\' && bash_dollar_squote) {
11171 c = decode_dollar_squote();
11172 if (c & 0x100) {
11173 USTPUTC('\\', out);
11174 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011175 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011176 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011177#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011178 USTPUTC(c, out);
11179 break;
11180 case CBACK: /* backslash */
11181 c = pgetc_without_PEOA();
11182 if (c == PEOF) {
11183 USTPUTC(CTLESC, out);
11184 USTPUTC('\\', out);
11185 pungetc();
11186 } else if (c == '\n') {
11187 setprompt_if(doprompt, 2);
11188 } else {
11189#if ENABLE_ASH_EXPAND_PRMT
11190 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011191 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011192 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011193 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011194#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011195 /* Backslash is retained if we are in "str" and next char isn't special */
11196 if (dblquote
11197 && c != '\\'
11198 && c != '`'
11199 && c != '$'
11200 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011201 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011202 USTPUTC(CTLESC, out);
11203 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011204 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011205 if (SIT(c, SQSYNTAX) == CCTL)
11206 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011207 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011208 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011209 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011210 break;
11211 case CSQUOTE:
11212 syntax = SQSYNTAX;
11213 quotemark:
11214 if (eofmark == NULL) {
11215 USTPUTC(CTLQUOTEMARK, out);
11216 }
11217 break;
11218 case CDQUOTE:
11219 syntax = DQSYNTAX;
11220 dblquote = 1;
11221 goto quotemark;
11222 case CENDQUOTE:
11223 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11224 if (eofmark != NULL && arinest == 0
11225 && varnest == 0
11226 ) {
11227 USTPUTC(c, out);
11228 } else {
11229 if (dqvarnest == 0) {
11230 syntax = BASESYNTAX;
11231 dblquote = 0;
11232 }
11233 quotef = 1;
11234 goto quotemark;
11235 }
11236 break;
11237 case CVAR: /* '$' */
11238 PARSESUB(); /* parse substitution */
11239 break;
11240 case CENDVAR: /* '}' */
11241 if (varnest > 0) {
11242 varnest--;
11243 if (dqvarnest > 0) {
11244 dqvarnest--;
11245 }
11246 c = CTLENDVAR;
11247 }
11248 USTPUTC(c, out);
11249 break;
11250#if ENABLE_SH_MATH_SUPPORT
11251 case CLP: /* '(' in arithmetic */
11252 parenlevel++;
11253 USTPUTC(c, out);
11254 break;
11255 case CRP: /* ')' in arithmetic */
11256 if (parenlevel > 0) {
11257 parenlevel--;
11258 } else {
11259 if (pgetc() == ')') {
11260 if (--arinest == 0) {
11261 syntax = prevsyntax;
11262 dblquote = (syntax == DQSYNTAX);
11263 c = CTLENDARI;
11264 }
11265 } else {
11266 /*
11267 * unbalanced parens
11268 * (don't 2nd guess - no error)
11269 */
11270 pungetc();
11271 }
11272 }
11273 USTPUTC(c, out);
11274 break;
11275#endif
11276 case CBQUOTE: /* '`' */
11277 PARSEBACKQOLD();
11278 break;
11279 case CENDFILE:
11280 goto endword; /* exit outer loop */
11281 case CIGN:
11282 break;
11283 default:
11284 if (varnest == 0) {
11285#if ENABLE_ASH_BASH_COMPAT
11286 if (c == '&') {
11287 if (pgetc() == '>')
11288 c = 0x100 + '>'; /* flag &> */
11289 pungetc();
11290 }
11291#endif
11292 goto endword; /* exit outer loop */
11293 }
11294 IF_ASH_ALIAS(if (c != PEOA))
11295 USTPUTC(c, out);
11296 }
11297 c = pgetc_fast();
11298 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011299 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011300
Mike Frysinger98c52642009-04-02 10:02:37 +000011301#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011302 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011303 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011304#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011305 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011306 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011307 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011308 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011309 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011310 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011311 }
11312 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011313 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011314 out = stackblock();
11315 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011316 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011317 && quotef == 0
11318 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011319 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011320 PARSEREDIR(); /* passed as params: out, c */
11321 lasttoken = TREDIR;
11322 return lasttoken;
11323 }
11324 /* else: non-number X seen, interpret it
11325 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011326 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011327 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011328 }
11329 quoteflag = quotef;
11330 backquotelist = bqlist;
11331 grabstackblock(len);
11332 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011333 lasttoken = TWORD;
11334 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011335/* end of readtoken routine */
11336
Eric Andersencb57d552001-06-28 07:25:16 +000011337/*
11338 * Check to see whether we are at the end of the here document. When this
11339 * is called, c is set to the first character of the next input line. If
11340 * we are at the end of the here document, this routine sets the c to PEOF.
11341 */
Eric Andersenc470f442003-07-28 09:56:35 +000011342checkend: {
11343 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011344#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011345 if (c == PEOA)
11346 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011347#endif
11348 if (striptabs) {
11349 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011350 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011351 }
Eric Andersenc470f442003-07-28 09:56:35 +000011352 }
11353 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011354 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011355 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011356
Eric Andersenc470f442003-07-28 09:56:35 +000011357 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011358 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11359 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011360 if (*p == '\n' && *q == '\0') {
11361 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011362 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011363 needprompt = doprompt;
11364 } else {
11365 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011366 }
11367 }
11368 }
11369 }
Eric Andersenc470f442003-07-28 09:56:35 +000011370 goto checkend_return;
11371}
Eric Andersencb57d552001-06-28 07:25:16 +000011372
Eric Andersencb57d552001-06-28 07:25:16 +000011373/*
11374 * Parse a redirection operator. The variable "out" points to a string
11375 * specifying the fd to be redirected. The variable "c" contains the
11376 * first character of the redirection operator.
11377 */
Eric Andersenc470f442003-07-28 09:56:35 +000011378parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011379 /* out is already checked to be a valid number or "" */
11380 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011381 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011382
Denis Vlasenko597906c2008-02-20 16:38:54 +000011383 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011384 if (c == '>') {
11385 np->nfile.fd = 1;
11386 c = pgetc();
11387 if (c == '>')
11388 np->type = NAPPEND;
11389 else if (c == '|')
11390 np->type = NCLOBBER;
11391 else if (c == '&')
11392 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011393 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011394 else {
11395 np->type = NTO;
11396 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011397 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011398 }
11399#if ENABLE_ASH_BASH_COMPAT
11400 else if (c == 0x100 + '>') { /* this flags &> redirection */
11401 np->nfile.fd = 1;
11402 pgetc(); /* this is '>', no need to check */
11403 np->type = NTO2;
11404 }
11405#endif
11406 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011407 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011408 c = pgetc();
11409 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011410 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011411 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011412 np = stzalloc(sizeof(struct nhere));
11413 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011414 }
11415 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011416 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011417 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011418 c = pgetc();
11419 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011420 heredoc->striptabs = 1;
11421 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011422 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011423 pungetc();
11424 }
11425 break;
11426
11427 case '&':
11428 np->type = NFROMFD;
11429 break;
11430
11431 case '>':
11432 np->type = NFROMTO;
11433 break;
11434
11435 default:
11436 np->type = NFROM;
11437 pungetc();
11438 break;
11439 }
Eric Andersencb57d552001-06-28 07:25:16 +000011440 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011441 if (fd >= 0)
11442 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011443 redirnode = np;
11444 goto parseredir_return;
11445}
Eric Andersencb57d552001-06-28 07:25:16 +000011446
Eric Andersencb57d552001-06-28 07:25:16 +000011447/*
11448 * Parse a substitution. At this point, we have read the dollar sign
11449 * and nothing else.
11450 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011451
11452/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11453 * (assuming ascii char codes, as the original implementation did) */
11454#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011455 (((unsigned)(c) - 33 < 32) \
11456 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011457parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011458 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011459 int typeloc;
11460 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011461
Eric Andersenc470f442003-07-28 09:56:35 +000011462 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011463 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011464 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011465 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011466#if ENABLE_ASH_BASH_COMPAT
11467 if (c == '\'')
11468 bash_dollar_squote = 1;
11469 else
11470#endif
11471 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011472 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011473 } else if (c == '(') {
11474 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011475 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011476#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011477 PARSEARITH();
11478#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011479 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011480#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011481 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011482 pungetc();
11483 PARSEBACKQNEW();
11484 }
11485 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011486 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011487 USTPUTC(CTLVAR, out);
11488 typeloc = out - (char *)stackblock();
11489 USTPUTC(VSNORMAL, out);
11490 subtype = VSNORMAL;
11491 if (c == '{') {
11492 c = pgetc();
11493 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011494 c = pgetc();
11495 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011496 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011497 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011498 subtype = VSLENGTH; /* ${#VAR} */
11499 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011500 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011501 }
Eric Andersenc470f442003-07-28 09:56:35 +000011502 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011503 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011504 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011505 do {
11506 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011507 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011508 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011509 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011510 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011511 do {
11512 STPUTC(c, out);
11513 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011514 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011515 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011516 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011517 USTPUTC(c, out);
11518 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011519 } else {
11520 badsub:
11521 raise_error_syntax("bad substitution");
11522 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011523 if (c != '}' && subtype == VSLENGTH) {
11524 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011525 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011526 }
Eric Andersencb57d552001-06-28 07:25:16 +000011527
Eric Andersenc470f442003-07-28 09:56:35 +000011528 STPUTC('=', out);
11529 flags = 0;
11530 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011531 /* ${VAR...} but not $VAR or ${#VAR} */
11532 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011533 switch (c) {
11534 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011535 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011536#if ENABLE_ASH_BASH_COMPAT
11537 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011538//TODO: support more general format ${v:EXPR:EXPR},
11539// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011540 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011541 pungetc();
11542 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011543 }
11544#endif
11545 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011546 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011547 default: {
11548 static const char types[] ALIGN1 = "}-+?=";
11549 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011550 if (p == NULL)
11551 goto badsub;
11552 subtype = p - types + VSNORMAL;
11553 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011554 }
Eric Andersenc470f442003-07-28 09:56:35 +000011555 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011556 case '#': {
11557 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011558 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011559 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011560 if (c != cc)
11561 goto do_pungetc;
11562 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011563 break;
11564 }
11565#if ENABLE_ASH_BASH_COMPAT
11566 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011567 /* ${v/[/]pattern/repl} */
11568//TODO: encode pattern and repl separately.
11569// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011570 subtype = VSREPLACE;
11571 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011572 if (c != '/')
11573 goto do_pungetc;
11574 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011575 break;
11576#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011577 }
Eric Andersenc470f442003-07-28 09:56:35 +000011578 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011579 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011580 pungetc();
11581 }
11582 if (dblquote || arinest)
11583 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011584 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011585 if (subtype != VSNORMAL) {
11586 varnest++;
11587 if (dblquote || arinest) {
11588 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011589 }
11590 }
11591 }
Eric Andersenc470f442003-07-28 09:56:35 +000011592 goto parsesub_return;
11593}
Eric Andersencb57d552001-06-28 07:25:16 +000011594
Eric Andersencb57d552001-06-28 07:25:16 +000011595/*
11596 * Called to parse command substitutions. Newstyle is set if the command
11597 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11598 * list of commands (passed by reference), and savelen is the number of
11599 * characters on the top of the stack which must be preserved.
11600 */
Eric Andersenc470f442003-07-28 09:56:35 +000011601parsebackq: {
11602 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011603 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011604 union node *n;
11605 char *volatile str;
11606 struct jmploc jmploc;
11607 struct jmploc *volatile savehandler;
11608 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011609 smallint saveprompt = 0;
11610
Eric Andersencb57d552001-06-28 07:25:16 +000011611#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011612 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011613#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011614 savepbq = parsebackquote;
11615 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011616 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011617 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011618 exception_handler = savehandler;
11619 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011620 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011621 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011622 str = NULL;
11623 savelen = out - (char *)stackblock();
11624 if (savelen > 0) {
11625 str = ckmalloc(savelen);
11626 memcpy(str, stackblock(), savelen);
11627 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011628 savehandler = exception_handler;
11629 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011630 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011631 if (oldstyle) {
11632 /* We must read until the closing backquote, giving special
11633 treatment to some slashes, and then push the string and
11634 reread it as input, interpreting it normally. */
11635 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011636 size_t psavelen;
11637 char *pstr;
11638
Eric Andersenc470f442003-07-28 09:56:35 +000011639 STARTSTACKSTR(pout);
11640 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011641 int pc;
11642
11643 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011644 pc = pgetc();
11645 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011646 case '`':
11647 goto done;
11648
11649 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011650 pc = pgetc();
11651 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011652 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011653 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011654 /*
11655 * If eating a newline, avoid putting
11656 * the newline into the new character
11657 * stream (via the STPUTC after the
11658 * switch).
11659 */
11660 continue;
11661 }
11662 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011663 && (!dblquote || pc != '"')
11664 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011665 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011666 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011667 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011668 break;
11669 }
11670 /* fall through */
11671
11672 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011673 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011674 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011675 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011676
11677 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011678 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011679 needprompt = doprompt;
11680 break;
11681
11682 default:
11683 break;
11684 }
11685 STPUTC(pc, pout);
11686 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011687 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011688 STPUTC('\0', pout);
11689 psavelen = pout - (char *)stackblock();
11690 if (psavelen > 0) {
11691 pstr = grabstackstr(pout);
11692 setinputstring(pstr);
11693 }
11694 }
11695 nlpp = &bqlist;
11696 while (*nlpp)
11697 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011698 *nlpp = stzalloc(sizeof(**nlpp));
11699 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011700 parsebackquote = oldstyle;
11701
11702 if (oldstyle) {
11703 saveprompt = doprompt;
11704 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011705 }
11706
Eric Andersenc470f442003-07-28 09:56:35 +000011707 n = list(2);
11708
11709 if (oldstyle)
11710 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011711 else if (readtoken() != TRP)
11712 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011713
11714 (*nlpp)->n = n;
11715 if (oldstyle) {
11716 /*
11717 * Start reading from old file again, ignoring any pushed back
11718 * tokens left from the backquote parsing
11719 */
11720 popfile();
11721 tokpushback = 0;
11722 }
11723 while (stackblocksize() <= savelen)
11724 growstackblock();
11725 STARTSTACKSTR(out);
11726 if (str) {
11727 memcpy(out, str, savelen);
11728 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011729 INT_OFF;
11730 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011731 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011732 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011733 }
11734 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011735 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011736 if (arinest || dblquote)
11737 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11738 else
11739 USTPUTC(CTLBACKQ, out);
11740 if (oldstyle)
11741 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011742 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011743}
11744
Mike Frysinger98c52642009-04-02 10:02:37 +000011745#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011746/*
11747 * Parse an arithmetic expansion (indicate start of one and set state)
11748 */
Eric Andersenc470f442003-07-28 09:56:35 +000011749parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011750 if (++arinest == 1) {
11751 prevsyntax = syntax;
11752 syntax = ARISYNTAX;
11753 USTPUTC(CTLARI, out);
11754 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011755 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011756 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011757 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011758 } else {
11759 /*
11760 * we collapse embedded arithmetic expansion to
11761 * parenthesis, which should be equivalent
11762 */
11763 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011764 }
Eric Andersenc470f442003-07-28 09:56:35 +000011765 goto parsearith_return;
11766}
11767#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011768
Eric Andersenc470f442003-07-28 09:56:35 +000011769} /* end of readtoken */
11770
Eric Andersencb57d552001-06-28 07:25:16 +000011771/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011772 * Read the next input token.
11773 * If the token is a word, we set backquotelist to the list of cmds in
11774 * backquotes. We set quoteflag to true if any part of the word was
11775 * quoted.
11776 * If the token is TREDIR, then we set redirnode to a structure containing
11777 * the redirection.
11778 * In all cases, the variable startlinno is set to the number of the line
11779 * on which the token starts.
11780 *
11781 * [Change comment: here documents and internal procedures]
11782 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11783 * word parsing code into a separate routine. In this case, readtoken
11784 * doesn't need to have any internal procedures, but parseword does.
11785 * We could also make parseoperator in essence the main routine, and
11786 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011787 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011788#define NEW_xxreadtoken
11789#ifdef NEW_xxreadtoken
11790/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011791static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011792 '\n', '(', ')', /* singles */
11793 '&', '|', ';', /* doubles */
11794 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011795};
Eric Andersencb57d552001-06-28 07:25:16 +000011796
Denis Vlasenko834dee72008-10-07 09:18:30 +000011797#define xxreadtoken_singles 3
11798#define xxreadtoken_doubles 3
11799
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011800static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011801 TNL, TLP, TRP, /* only single occurrence allowed */
11802 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11803 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011804 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011805};
11806
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011807static int
11808xxreadtoken(void)
11809{
11810 int c;
11811
11812 if (tokpushback) {
11813 tokpushback = 0;
11814 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011815 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011816 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011817 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011818 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011819 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011820 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011821 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011822
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011823 if (c == '#') {
11824 while ((c = pgetc()) != '\n' && c != PEOF)
11825 continue;
11826 pungetc();
11827 } else if (c == '\\') {
11828 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011829 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011830 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011831 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011832 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011833 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011834 } else {
11835 const char *p;
11836
11837 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11838 if (c != PEOF) {
11839 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011840 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011841 needprompt = doprompt;
11842 }
11843
11844 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011845 if (p == NULL)
11846 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011847
Denis Vlasenko834dee72008-10-07 09:18:30 +000011848 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11849 int cc = pgetc();
11850 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011851 p += xxreadtoken_doubles + 1;
11852 } else {
11853 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011854#if ENABLE_ASH_BASH_COMPAT
11855 if (c == '&' && cc == '>') /* &> */
11856 break; /* return readtoken1(...) */
11857#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011858 }
11859 }
11860 }
11861 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11862 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011863 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011864 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011865
11866 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011867}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011868#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011869#define RETURN(token) return lasttoken = token
11870static int
11871xxreadtoken(void)
11872{
11873 int c;
11874
11875 if (tokpushback) {
11876 tokpushback = 0;
11877 return lasttoken;
11878 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011879 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011880 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011881 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011882 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011883 switch (c) {
11884 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011885 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011886 continue;
11887 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011888 while ((c = pgetc()) != '\n' && c != PEOF)
11889 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011890 pungetc();
11891 continue;
11892 case '\\':
11893 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011894 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011895 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011896 continue;
11897 }
11898 pungetc();
11899 goto breakloop;
11900 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011901 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011902 needprompt = doprompt;
11903 RETURN(TNL);
11904 case PEOF:
11905 RETURN(TEOF);
11906 case '&':
11907 if (pgetc() == '&')
11908 RETURN(TAND);
11909 pungetc();
11910 RETURN(TBACKGND);
11911 case '|':
11912 if (pgetc() == '|')
11913 RETURN(TOR);
11914 pungetc();
11915 RETURN(TPIPE);
11916 case ';':
11917 if (pgetc() == ';')
11918 RETURN(TENDCASE);
11919 pungetc();
11920 RETURN(TSEMI);
11921 case '(':
11922 RETURN(TLP);
11923 case ')':
11924 RETURN(TRP);
11925 default:
11926 goto breakloop;
11927 }
11928 }
11929 breakloop:
11930 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11931#undef RETURN
11932}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011933#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011934
11935static int
11936readtoken(void)
11937{
11938 int t;
11939#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011940 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011941#endif
11942
11943#if ENABLE_ASH_ALIAS
11944 top:
11945#endif
11946
11947 t = xxreadtoken();
11948
11949 /*
11950 * eat newlines
11951 */
11952 if (checkkwd & CHKNL) {
11953 while (t == TNL) {
11954 parseheredoc();
11955 t = xxreadtoken();
11956 }
11957 }
11958
11959 if (t != TWORD || quoteflag) {
11960 goto out;
11961 }
11962
11963 /*
11964 * check for keywords
11965 */
11966 if (checkkwd & CHKKWD) {
11967 const char *const *pp;
11968
11969 pp = findkwd(wordtext);
11970 if (pp) {
11971 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011972 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011973 goto out;
11974 }
11975 }
11976
11977 if (checkkwd & CHKALIAS) {
11978#if ENABLE_ASH_ALIAS
11979 struct alias *ap;
11980 ap = lookupalias(wordtext, 1);
11981 if (ap != NULL) {
11982 if (*ap->val) {
11983 pushstring(ap->val, ap);
11984 }
11985 goto top;
11986 }
11987#endif
11988 }
11989 out:
11990 checkkwd = 0;
11991#if DEBUG
11992 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011993 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011994 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011995 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011996#endif
11997 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011998}
11999
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012000static char
12001peektoken(void)
12002{
12003 int t;
12004
12005 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012006 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012007 return tokname_array[t][0];
12008}
Eric Andersencb57d552001-06-28 07:25:16 +000012009
12010/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012011 * Read and parse a command. Returns NODE_EOF on end of file.
12012 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000012013 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012014static union node *
12015parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000012016{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012017 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000012018
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012019 tokpushback = 0;
12020 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012021 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012022 needprompt = 0;
12023 t = readtoken();
12024 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012025 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012026 if (t == TNL)
12027 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012028 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012029 return list(1);
12030}
12031
12032/*
12033 * Input any here documents.
12034 */
12035static void
12036parseheredoc(void)
12037{
12038 struct heredoc *here;
12039 union node *n;
12040
12041 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012042 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012043
12044 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012045 setprompt_if(needprompt, 2);
12046 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012047 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012048 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012049 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012050 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012051 n->narg.text = wordtext;
12052 n->narg.backquote = backquotelist;
12053 here->here->nhere.doc = n;
12054 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012055 }
Eric Andersencb57d552001-06-28 07:25:16 +000012056}
12057
12058
12059/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012060 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012061 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012062#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012063static const char *
12064expandstr(const char *ps)
12065{
12066 union node n;
12067
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012068 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12069 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012070 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012071 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012072 popfile();
12073
12074 n.narg.type = NARG;
12075 n.narg.next = NULL;
12076 n.narg.text = wordtext;
12077 n.narg.backquote = backquotelist;
12078
12079 expandarg(&n, NULL, 0);
12080 return stackblock();
12081}
12082#endif
12083
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012084/*
12085 * Execute a command or commands contained in a string.
12086 */
12087static int
12088evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012089{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012090 union node *n;
12091 struct stackmark smark;
12092 int skip;
12093
12094 setinputstring(s);
12095 setstackmark(&smark);
12096
12097 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012098 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012099 evaltree(n, 0);
12100 popstackmark(&smark);
12101 skip = evalskip;
12102 if (skip)
12103 break;
12104 }
12105 popfile();
12106
12107 skip &= mask;
12108 evalskip = skip;
12109 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012110}
12111
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012112/*
12113 * The eval command.
12114 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012115static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012116evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012117{
12118 char *p;
12119 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012120
Denis Vlasenko68404f12008-03-17 09:00:54 +000012121 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012122 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012123 argv += 2;
12124 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012125 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012126 for (;;) {
12127 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012128 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012129 if (p == NULL)
12130 break;
12131 STPUTC(' ', concat);
12132 }
12133 STPUTC('\0', concat);
12134 p = grabstackstr(concat);
12135 }
12136 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012137 }
12138 return exitstatus;
12139}
12140
12141/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012142 * Read and execute commands.
12143 * "Top" is nonzero for the top level command loop;
12144 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012145 */
12146static int
12147cmdloop(int top)
12148{
12149 union node *n;
12150 struct stackmark smark;
12151 int inter;
12152 int numeof = 0;
12153
12154 TRACE(("cmdloop(%d) called\n", top));
12155 for (;;) {
12156 int skip;
12157
12158 setstackmark(&smark);
12159#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012160 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012161 showjobs(stderr, SHOW_CHANGED);
12162#endif
12163 inter = 0;
12164 if (iflag && top) {
12165 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012166 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012167 }
12168 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012169#if DEBUG
12170 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012171 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012172#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012173 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012174 if (!top || numeof >= 50)
12175 break;
12176 if (!stoppedjobs()) {
12177 if (!Iflag)
12178 break;
12179 out2str("\nUse \"exit\" to leave shell.\n");
12180 }
12181 numeof++;
12182 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012183 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12184 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012185 numeof = 0;
12186 evaltree(n, 0);
12187 }
12188 popstackmark(&smark);
12189 skip = evalskip;
12190
12191 if (skip) {
12192 evalskip = 0;
12193 return skip & SKIPEVAL;
12194 }
12195 }
12196 return 0;
12197}
12198
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012199/*
12200 * Take commands from a file. To be compatible we should do a path
12201 * search for the file, which is necessary to find sub-commands.
12202 */
12203static char *
12204find_dot_file(char *name)
12205{
12206 char *fullname;
12207 const char *path = pathval();
12208 struct stat statb;
12209
12210 /* don't try this for absolute or relative paths */
12211 if (strchr(name, '/'))
12212 return name;
12213
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012214 /* IIRC standards do not say whether . is to be searched.
12215 * And it is even smaller this way, making it unconditional for now:
12216 */
12217 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12218 fullname = name;
12219 goto try_cur_dir;
12220 }
12221
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012222 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012223 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012224 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12225 /*
12226 * Don't bother freeing here, since it will
12227 * be freed by the caller.
12228 */
12229 return fullname;
12230 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012231 if (fullname != name)
12232 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012233 }
12234
12235 /* not found in the PATH */
12236 ash_msg_and_raise_error("%s: not found", name);
12237 /* NOTREACHED */
12238}
12239
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012240static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012241dotcmd(int argc, char **argv)
12242{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012243 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012244 struct strlist *sp;
12245 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012246
12247 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012248 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012249
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012250 if (!argv[1]) {
12251 /* bash says: "bash: .: filename argument required" */
12252 return 2; /* bash compat */
12253 }
12254
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012255 /* "false; . empty_file; echo $?" should print 0, not 1: */
12256 exitstatus = 0;
12257
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012258 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012259
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012260 argv += 2;
12261 argc -= 2;
12262 if (argc) { /* argc > 0, argv[0] != NULL */
12263 saveparam = shellparam;
12264 shellparam.malloced = 0;
12265 shellparam.nparam = argc;
12266 shellparam.p = argv;
12267 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012268
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012269 setinputfile(fullname, INPUT_PUSH_FILE);
12270 commandname = fullname;
12271 cmdloop(0);
12272 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012273
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012274 if (argc) {
12275 freeparam(&shellparam);
12276 shellparam = saveparam;
12277 };
12278
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012279 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012280}
12281
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012282static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012283exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012284{
12285 if (stoppedjobs())
12286 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012287 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012288 exitstatus = number(argv[1]);
12289 raise_exception(EXEXIT);
12290 /* NOTREACHED */
12291}
12292
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012293/*
12294 * Read a file containing shell functions.
12295 */
12296static void
12297readcmdfile(char *name)
12298{
12299 setinputfile(name, INPUT_PUSH_FILE);
12300 cmdloop(0);
12301 popfile();
12302}
12303
12304
Denis Vlasenkocc571512007-02-23 21:10:35 +000012305/* ============ find_command inplementation */
12306
12307/*
12308 * Resolve a command name. If you change this routine, you may have to
12309 * change the shellexec routine as well.
12310 */
12311static void
12312find_command(char *name, struct cmdentry *entry, int act, const char *path)
12313{
12314 struct tblentry *cmdp;
12315 int idx;
12316 int prev;
12317 char *fullname;
12318 struct stat statb;
12319 int e;
12320 int updatetbl;
12321 struct builtincmd *bcmd;
12322
12323 /* If name contains a slash, don't use PATH or hash table */
12324 if (strchr(name, '/') != NULL) {
12325 entry->u.index = -1;
12326 if (act & DO_ABS) {
12327 while (stat(name, &statb) < 0) {
12328#ifdef SYSV
12329 if (errno == EINTR)
12330 continue;
12331#endif
12332 entry->cmdtype = CMDUNKNOWN;
12333 return;
12334 }
12335 }
12336 entry->cmdtype = CMDNORMAL;
12337 return;
12338 }
12339
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012340/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012341
12342 updatetbl = (path == pathval());
12343 if (!updatetbl) {
12344 act |= DO_ALTPATH;
12345 if (strstr(path, "%builtin") != NULL)
12346 act |= DO_ALTBLTIN;
12347 }
12348
12349 /* If name is in the table, check answer will be ok */
12350 cmdp = cmdlookup(name, 0);
12351 if (cmdp != NULL) {
12352 int bit;
12353
12354 switch (cmdp->cmdtype) {
12355 default:
12356#if DEBUG
12357 abort();
12358#endif
12359 case CMDNORMAL:
12360 bit = DO_ALTPATH;
12361 break;
12362 case CMDFUNCTION:
12363 bit = DO_NOFUNC;
12364 break;
12365 case CMDBUILTIN:
12366 bit = DO_ALTBLTIN;
12367 break;
12368 }
12369 if (act & bit) {
12370 updatetbl = 0;
12371 cmdp = NULL;
12372 } else if (cmdp->rehash == 0)
12373 /* if not invalidated by cd, we're done */
12374 goto success;
12375 }
12376
12377 /* If %builtin not in path, check for builtin next */
12378 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012379 if (bcmd) {
12380 if (IS_BUILTIN_REGULAR(bcmd))
12381 goto builtin_success;
12382 if (act & DO_ALTPATH) {
12383 if (!(act & DO_ALTBLTIN))
12384 goto builtin_success;
12385 } else if (builtinloc <= 0) {
12386 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012387 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012388 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012389
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012390#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012391 {
12392 int applet_no = find_applet_by_name(name);
12393 if (applet_no >= 0) {
12394 entry->cmdtype = CMDNORMAL;
12395 entry->u.index = -2 - applet_no;
12396 return;
12397 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012398 }
12399#endif
12400
Denis Vlasenkocc571512007-02-23 21:10:35 +000012401 /* We have to search path. */
12402 prev = -1; /* where to start */
12403 if (cmdp && cmdp->rehash) { /* doing a rehash */
12404 if (cmdp->cmdtype == CMDBUILTIN)
12405 prev = builtinloc;
12406 else
12407 prev = cmdp->param.index;
12408 }
12409
12410 e = ENOENT;
12411 idx = -1;
12412 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012413 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012414 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012415 /* NB: code below will still use fullname
12416 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012417 idx++;
12418 if (pathopt) {
12419 if (prefix(pathopt, "builtin")) {
12420 if (bcmd)
12421 goto builtin_success;
12422 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012423 }
12424 if ((act & DO_NOFUNC)
12425 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012426 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012427 continue;
12428 }
12429 }
12430 /* if rehash, don't redo absolute path names */
12431 if (fullname[0] == '/' && idx <= prev) {
12432 if (idx < prev)
12433 continue;
12434 TRACE(("searchexec \"%s\": no change\n", name));
12435 goto success;
12436 }
12437 while (stat(fullname, &statb) < 0) {
12438#ifdef SYSV
12439 if (errno == EINTR)
12440 continue;
12441#endif
12442 if (errno != ENOENT && errno != ENOTDIR)
12443 e = errno;
12444 goto loop;
12445 }
12446 e = EACCES; /* if we fail, this will be the error */
12447 if (!S_ISREG(statb.st_mode))
12448 continue;
12449 if (pathopt) { /* this is a %func directory */
12450 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012451 /* NB: stalloc will return space pointed by fullname
12452 * (because we don't have any intervening allocations
12453 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012454 readcmdfile(fullname);
12455 cmdp = cmdlookup(name, 0);
12456 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12457 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12458 stunalloc(fullname);
12459 goto success;
12460 }
12461 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12462 if (!updatetbl) {
12463 entry->cmdtype = CMDNORMAL;
12464 entry->u.index = idx;
12465 return;
12466 }
12467 INT_OFF;
12468 cmdp = cmdlookup(name, 1);
12469 cmdp->cmdtype = CMDNORMAL;
12470 cmdp->param.index = idx;
12471 INT_ON;
12472 goto success;
12473 }
12474
12475 /* We failed. If there was an entry for this command, delete it */
12476 if (cmdp && updatetbl)
12477 delete_cmd_entry();
12478 if (act & DO_ERR)
12479 ash_msg("%s: %s", name, errmsg(e, "not found"));
12480 entry->cmdtype = CMDUNKNOWN;
12481 return;
12482
12483 builtin_success:
12484 if (!updatetbl) {
12485 entry->cmdtype = CMDBUILTIN;
12486 entry->u.cmd = bcmd;
12487 return;
12488 }
12489 INT_OFF;
12490 cmdp = cmdlookup(name, 1);
12491 cmdp->cmdtype = CMDBUILTIN;
12492 cmdp->param.cmd = bcmd;
12493 INT_ON;
12494 success:
12495 cmdp->rehash = 0;
12496 entry->cmdtype = cmdp->cmdtype;
12497 entry->u = cmdp->param;
12498}
12499
12500
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012501/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012502
Eric Andersencb57d552001-06-28 07:25:16 +000012503/*
Eric Andersencb57d552001-06-28 07:25:16 +000012504 * The trap builtin.
12505 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012506static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012507trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012508{
12509 char *action;
12510 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012511 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012512
Eric Andersenc470f442003-07-28 09:56:35 +000012513 nextopt(nullstr);
12514 ap = argptr;
12515 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012516 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012517 char *tr = trap_ptr[signo];
12518 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012519 /* note: bash adds "SIG", but only if invoked
12520 * as "bash". If called as "sh", or if set -o posix,
12521 * then it prints short signal names.
12522 * We are printing short names: */
12523 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012524 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012525 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012526 /* trap_ptr != trap only if we are in special-cased `trap` code.
12527 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012528 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012529 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012530 }
12531 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012532 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012533 if (trap_ptr != trap) {
12534 free(trap_ptr);
12535 trap_ptr = trap;
12536 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012537 */
Eric Andersencb57d552001-06-28 07:25:16 +000012538 return 0;
12539 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012540
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012541 action = NULL;
12542 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012543 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012544 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012545 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012546 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012547 if (signo < 0) {
12548 /* Mimic bash message exactly */
12549 ash_msg("%s: invalid signal specification", *ap);
12550 exitcode = 1;
12551 goto next;
12552 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012553 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012554 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012555 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012556 action = NULL;
12557 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012558 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012559 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012560 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012561 if (action)
12562 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012563 trap[signo] = action;
12564 if (signo != 0)
12565 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012566 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012567 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012568 ap++;
12569 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012570 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012571}
12572
Eric Andersenc470f442003-07-28 09:56:35 +000012573
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012574/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012575
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012576#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012577/*
12578 * Lists available builtins
12579 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012580static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012581helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012582{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012583 unsigned col;
12584 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012585
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012586 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012587 "Built-in commands:\n"
12588 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012589 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012590 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012591 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012592 if (col > 60) {
12593 out1fmt("\n");
12594 col = 0;
12595 }
12596 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012597#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012598 {
12599 const char *a = applet_names;
12600 while (*a) {
12601 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12602 if (col > 60) {
12603 out1fmt("\n");
12604 col = 0;
12605 }
12606 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012607 }
12608 }
12609#endif
12610 out1fmt("\n\n");
12611 return EXIT_SUCCESS;
12612}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012613#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012614
Eric Andersencb57d552001-06-28 07:25:16 +000012615/*
Eric Andersencb57d552001-06-28 07:25:16 +000012616 * The export and readonly commands.
12617 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012618static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012619exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012620{
12621 struct var *vp;
12622 char *name;
12623 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012624 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012625 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012626
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012627 if (nextopt("p") != 'p') {
12628 aptr = argptr;
12629 name = *aptr;
12630 if (name) {
12631 do {
12632 p = strchr(name, '=');
12633 if (p != NULL) {
12634 p++;
12635 } else {
12636 vp = *findvar(hashvar(name), name);
12637 if (vp) {
12638 vp->flags |= flag;
12639 continue;
12640 }
Eric Andersencb57d552001-06-28 07:25:16 +000012641 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012642 setvar(name, p, flag);
12643 } while ((name = *++aptr) != NULL);
12644 return 0;
12645 }
Eric Andersencb57d552001-06-28 07:25:16 +000012646 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012647 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012648 return 0;
12649}
12650
Eric Andersencb57d552001-06-28 07:25:16 +000012651/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012652 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012653 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012654static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012655unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012656{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012657 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012658
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012659 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012660 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012661 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012662}
12663
Eric Andersencb57d552001-06-28 07:25:16 +000012664/*
Eric Andersencb57d552001-06-28 07:25:16 +000012665 * The unset builtin command. We unset the function before we unset the
12666 * variable to allow a function to be unset when there is a readonly variable
12667 * with the same name.
12668 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012669static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012670unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012671{
12672 char **ap;
12673 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012674 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012675 int ret = 0;
12676
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012677 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012678 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012679 }
Eric Andersencb57d552001-06-28 07:25:16 +000012680
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012681 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012682 if (flag != 'f') {
12683 i = unsetvar(*ap);
12684 ret |= i;
12685 if (!(i & 2))
12686 continue;
12687 }
12688 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012689 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012690 }
Eric Andersenc470f442003-07-28 09:56:35 +000012691 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012692}
12693
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012694static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012695 ' ', offsetof(struct tms, tms_utime),
12696 '\n', offsetof(struct tms, tms_stime),
12697 ' ', offsetof(struct tms, tms_cutime),
12698 '\n', offsetof(struct tms, tms_cstime),
12699 0
12700};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012701static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012702timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012703{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012704 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012705 const unsigned char *p;
12706 struct tms buf;
12707
12708 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012709 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012710
12711 p = timescmd_str;
12712 do {
12713 t = *(clock_t *)(((char *) &buf) + p[1]);
12714 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012715 t = t % clk_tck;
12716 out1fmt("%lum%lu.%03lus%c",
12717 s / 60, s % 60,
12718 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012719 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012720 p += 2;
12721 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012722
Eric Andersencb57d552001-06-28 07:25:16 +000012723 return 0;
12724}
12725
Mike Frysinger98c52642009-04-02 10:02:37 +000012726#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012727/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012728 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012729 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012730 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012731 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012732 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012733static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012734letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012735{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012736 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012737
Denis Vlasenko68404f12008-03-17 09:00:54 +000012738 argv++;
12739 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012740 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012741 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012742 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012743 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012744
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012745 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012746}
Eric Andersenc470f442003-07-28 09:56:35 +000012747#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012748
Eric Andersenc470f442003-07-28 09:56:35 +000012749/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012750 * The read builtin. Options:
12751 * -r Do not interpret '\' specially
12752 * -s Turn off echo (tty only)
12753 * -n NCHARS Read NCHARS max
12754 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12755 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12756 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012757 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012758 * TODO: bash also has:
12759 * -a ARRAY Read into array[0],[1],etc
12760 * -d DELIM End on DELIM char, not newline
12761 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012762 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012763static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012764readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012765{
Denys Vlasenko73067272010-01-12 22:11:24 +010012766 char *opt_n = NULL;
12767 char *opt_p = NULL;
12768 char *opt_t = NULL;
12769 char *opt_u = NULL;
12770 int read_flags = 0;
12771 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012772 int i;
12773
Denys Vlasenko73067272010-01-12 22:11:24 +010012774 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012775 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012776 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012777 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012778 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012779 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012780 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012781 break;
12782 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012783 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012784 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012785 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012786 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012787 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012788 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012789 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012790 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012791 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012792 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012793 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012794 default:
12795 break;
12796 }
Eric Andersenc470f442003-07-28 09:56:35 +000012797 }
Paul Fox02eb9342005-09-07 16:56:02 +000012798
Denys Vlasenko03dad222010-01-12 23:29:57 +010012799 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012800 argptr,
12801 bltinlookup("IFS"), /* can be NULL */
12802 read_flags,
12803 opt_n,
12804 opt_p,
12805 opt_t,
12806 opt_u
12807 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012808
Denys Vlasenko73067272010-01-12 22:11:24 +010012809 if ((uintptr_t)r > 1)
12810 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012811
Denys Vlasenko73067272010-01-12 22:11:24 +010012812 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012813}
12814
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012815static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012816umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012817{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012818 static const char permuser[3] ALIGN1 = "ugo";
12819 static const char permmode[3] ALIGN1 = "rwx";
12820 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012821 S_IRUSR, S_IWUSR, S_IXUSR,
12822 S_IRGRP, S_IWGRP, S_IXGRP,
12823 S_IROTH, S_IWOTH, S_IXOTH
12824 };
12825
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012826 /* TODO: use bb_parse_mode() instead */
12827
Eric Andersenc470f442003-07-28 09:56:35 +000012828 char *ap;
12829 mode_t mask;
12830 int i;
12831 int symbolic_mode = 0;
12832
12833 while (nextopt("S") != '\0') {
12834 symbolic_mode = 1;
12835 }
12836
Denis Vlasenkob012b102007-02-19 22:43:01 +000012837 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012838 mask = umask(0);
12839 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012840 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012841
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012842 ap = *argptr;
12843 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012844 if (symbolic_mode) {
12845 char buf[18];
12846 char *p = buf;
12847
12848 for (i = 0; i < 3; i++) {
12849 int j;
12850
12851 *p++ = permuser[i];
12852 *p++ = '=';
12853 for (j = 0; j < 3; j++) {
12854 if ((mask & permmask[3 * i + j]) == 0) {
12855 *p++ = permmode[j];
12856 }
12857 }
12858 *p++ = ',';
12859 }
12860 *--p = 0;
12861 puts(buf);
12862 } else {
12863 out1fmt("%.4o\n", mask);
12864 }
12865 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012866 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012867 mask = 0;
12868 do {
12869 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012870 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012871 mask = (mask << 3) + (*ap - '0');
12872 } while (*++ap != '\0');
12873 umask(mask);
12874 } else {
12875 mask = ~mask & 0777;
12876 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012877 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012878 }
12879 umask(~mask & 0777);
12880 }
12881 }
12882 return 0;
12883}
12884
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012885static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012886ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012887{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012888 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012889}
12890
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012891/* ============ main() and helpers */
12892
12893/*
12894 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012895 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012896static void
12897exitshell(void)
12898{
12899 struct jmploc loc;
12900 char *p;
12901 int status;
12902
Denys Vlasenkobede2152011-09-04 16:12:33 +020012903#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
12904 save_history(line_input_state);
12905#endif
12906
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012907 status = exitstatus;
12908 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12909 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012910 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012911/* dash bug: it just does _exit(exitstatus) here
12912 * but we have to do setjobctl(0) first!
12913 * (bug is still not fixed in dash-0.5.3 - if you run dash
12914 * under Midnight Commander, on exit from dash MC is backgrounded) */
12915 status = exitstatus;
12916 goto out;
12917 }
12918 exception_handler = &loc;
12919 p = trap[0];
12920 if (p) {
12921 trap[0] = NULL;
12922 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012923 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012924 }
12925 flush_stdout_stderr();
12926 out:
12927 setjobctl(0);
12928 _exit(status);
12929 /* NOTREACHED */
12930}
12931
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012932static void
12933init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012934{
12935 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012936 /* we will never free this */
12937 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012938
12939 /* from trap.c: */
12940 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012941 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12942 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12943 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012944 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012945
12946 /* from var.c: */
12947 {
12948 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012949 const char *p;
12950 struct stat st1, st2;
12951
12952 initvar();
12953 for (envp = environ; envp && *envp; envp++) {
12954 if (strchr(*envp, '=')) {
12955 setvareq(*envp, VEXPORT|VTEXTFIXED);
12956 }
12957 }
12958
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012959 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012960
12961 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012962 if (p) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012963 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012964 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
12965 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012966 p = '\0';
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012967 }
12968 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012969 setpwd(p, 0);
12970 }
12971}
12972
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012973
12974//usage:#define ash_trivial_usage
Denys Vlasenko6b6af532011-03-08 10:24:17 +010012975//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012976//usage:#define ash_full_usage "\n\n"
12977//usage: "Unix shell interpreter"
12978
12979//usage:#if ENABLE_FEATURE_SH_IS_ASH
12980//usage:# define sh_trivial_usage ash_trivial_usage
12981//usage:# define sh_full_usage ash_full_usage
12982//usage:#endif
12983//usage:#if ENABLE_FEATURE_BASH_IS_ASH
12984//usage:# define bash_trivial_usage ash_trivial_usage
12985//usage:# define bash_full_usage ash_full_usage
12986//usage:#endif
12987
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012988/*
12989 * Process the shell command line arguments.
12990 */
12991static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012992procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012993{
12994 int i;
12995 const char *xminusc;
12996 char **xargv;
12997
12998 xargv = argv;
12999 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013000 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013001 xargv++;
13002 for (i = 0; i < NOPTS; i++)
13003 optlist[i] = 2;
13004 argptr = xargv;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013005 if (options(/*cmdline:*/ 1)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013006 /* it already printed err message */
13007 raise_exception(EXERROR);
13008 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013009 xargv = argptr;
13010 xminusc = minusc;
13011 if (*xargv == NULL) {
13012 if (xminusc)
13013 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13014 sflag = 1;
13015 }
13016 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13017 iflag = 1;
13018 if (mflag == 2)
13019 mflag = iflag;
13020 for (i = 0; i < NOPTS; i++)
13021 if (optlist[i] == 2)
13022 optlist[i] = 0;
13023#if DEBUG == 2
13024 debug = 1;
13025#endif
13026 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13027 if (xminusc) {
13028 minusc = *xargv++;
13029 if (*xargv)
13030 goto setarg0;
13031 } else if (!sflag) {
13032 setinputfile(*xargv, 0);
13033 setarg0:
13034 arg0 = *xargv++;
13035 commandname = arg0;
13036 }
13037
13038 shellparam.p = xargv;
13039#if ENABLE_ASH_GETOPTS
13040 shellparam.optind = 1;
13041 shellparam.optoff = -1;
13042#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013043 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013044 while (*xargv) {
13045 shellparam.nparam++;
13046 xargv++;
13047 }
13048 optschanged();
13049}
13050
13051/*
13052 * Read /etc/profile or .profile.
13053 */
13054static void
13055read_profile(const char *name)
13056{
13057 int skip;
13058
13059 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13060 return;
13061 skip = cmdloop(0);
13062 popfile();
13063 if (skip)
13064 exitshell();
13065}
13066
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013067/*
13068 * This routine is called when an error or an interrupt occurs in an
13069 * interactive shell and control is returned to the main command loop.
13070 */
13071static void
13072reset(void)
13073{
13074 /* from eval.c: */
13075 evalskip = 0;
13076 loopnest = 0;
13077 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013078 g_parsefile->left_in_buffer = 0;
13079 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013080 popallfiles();
13081 /* from parser.c: */
13082 tokpushback = 0;
13083 checkkwd = 0;
13084 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013085 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013086}
13087
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013088#if PROFILE
13089static short profile_buf[16384];
13090extern int etext();
13091#endif
13092
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013093/*
13094 * Main routine. We initialize things, parse the arguments, execute
13095 * profiles if we're a login shell, and then call cmdloop to execute
13096 * commands. The setjmp call sets up the location to jump to when an
13097 * exception occurs. When an exception occurs the variable "state"
13098 * is used to figure out how far we had gotten.
13099 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013100int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013101int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013102{
Mike Frysinger98c52642009-04-02 10:02:37 +000013103 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013104 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013105 struct jmploc jmploc;
13106 struct stackmark smark;
13107
Denis Vlasenko01631112007-12-16 17:20:38 +000013108 /* Initialize global data */
13109 INIT_G_misc();
13110 INIT_G_memstack();
13111 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013112#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013113 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013114#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013115 INIT_G_cmdtable();
13116
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013117#if PROFILE
13118 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13119#endif
13120
13121#if ENABLE_FEATURE_EDITING
13122 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13123#endif
13124 state = 0;
13125 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013126 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013127 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013128
13129 reset();
13130
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013131 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013132 if (e == EXERROR)
13133 exitstatus = 2;
13134 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013135 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013136 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013137 }
13138 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013139 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013140 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013141
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013142 popstackmark(&smark);
13143 FORCE_INT_ON; /* enable interrupts */
13144 if (s == 1)
13145 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013146 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013147 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013148 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013149 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013150 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013151 }
13152 exception_handler = &jmploc;
13153#if DEBUG
13154 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013155 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013156 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013157#endif
13158 rootpid = getpid();
13159
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013160 init();
13161 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013162 procargs(argv);
13163
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013164#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13165 if (iflag) {
13166 const char *hp = lookupvar("HISTFILE");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013167 if (!hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013168 hp = lookupvar("HOME");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013169 if (hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013170 char *defhp = concat_path_file(hp, ".ash_history");
13171 setvar("HISTFILE", defhp, 0);
13172 free(defhp);
13173 }
13174 }
13175 }
13176#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013177 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013178 isloginsh = 1;
13179 if (isloginsh) {
13180 state = 1;
13181 read_profile("/etc/profile");
13182 state1:
13183 state = 2;
13184 read_profile(".profile");
13185 }
13186 state2:
13187 state = 3;
13188 if (
13189#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013190 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013191#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013192 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013193 ) {
13194 shinit = lookupvar("ENV");
13195 if (shinit != NULL && *shinit != '\0') {
13196 read_profile(shinit);
13197 }
13198 }
13199 state3:
13200 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013201 if (minusc) {
13202 /* evalstring pushes parsefile stack.
13203 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013204 * is one of stacked source fds.
13205 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013206 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013207 // ^^ not necessary since now we special-case fd 0
13208 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013209 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013210 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013211
13212 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020013213#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013214 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013215 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013216 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013217 line_input_state->hist_file = hp;
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013218# if ENABLE_FEATURE_SH_HISTFILESIZE
13219 hp = lookupvar("HISTFILESIZE");
13220 line_input_state->max_history = size_from_HISTFILESIZE(hp);
13221# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013222 }
13223#endif
13224 state4: /* XXX ??? - why isn't this before the "if" statement */
13225 cmdloop(1);
13226 }
13227#if PROFILE
13228 monitor(0);
13229#endif
13230#ifdef GPROF
13231 {
13232 extern void _mcleanup(void);
13233 _mcleanup();
13234 }
13235#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013236 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013237 exitshell();
13238 /* NOTREACHED */
13239}
13240
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013241
Eric Andersendf82f612001-06-28 07:46:40 +000013242/*-
13243 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013244 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013245 *
13246 * This code is derived from software contributed to Berkeley by
13247 * Kenneth Almquist.
13248 *
13249 * Redistribution and use in source and binary forms, with or without
13250 * modification, are permitted provided that the following conditions
13251 * are met:
13252 * 1. Redistributions of source code must retain the above copyright
13253 * notice, this list of conditions and the following disclaimer.
13254 * 2. Redistributions in binary form must reproduce the above copyright
13255 * notice, this list of conditions and the following disclaimer in the
13256 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013257 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013258 * may be used to endorse or promote products derived from this software
13259 * without specific prior written permission.
13260 *
13261 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13262 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13263 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13264 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13265 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13266 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13267 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13268 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13269 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13270 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13271 * SUCH DAMAGE.
13272 */