blob: bfbd60d78cbd4efd892c60ec0085620e7a6ffa13 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000039#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000040#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020043#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010044
45#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010046#if ENABLE_SH_MATH_SUPPORT
47# include "math.h"
48#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020049#if ENABLE_ASH_RANDOM_SUPPORT
50# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020051#else
52# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020053#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000054
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020055#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010056#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000057/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020058# undef CONFIG_FEATURE_SH_STANDALONE
59# undef ENABLE_FEATURE_SH_STANDALONE
60# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010061# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020062# define ENABLE_FEATURE_SH_STANDALONE 0
63# define IF_FEATURE_SH_STANDALONE(...)
64# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000065#endif
66
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000067#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000068# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000069#endif
70
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010071#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000072# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000073#endif
74
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +010075//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
76//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
77//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
Denys Vlasenko771f1992010-07-16 14:31:34 +020078
Denys Vlasenkod383b492010-09-06 10:22:13 +020079//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko771f1992010-07-16 14:31:34 +020080//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
81
82//config:config ASH
83//config: bool "ash"
84//config: default y
85//config: depends on !NOMMU
86//config: help
87//config: Tha 'ash' shell adds about 60k in the default configuration and is
88//config: the most complete and most pedantically correct shell included with
89//config: busybox. This shell is actually a derivative of the Debian 'dash'
90//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
91//config: (written by Kenneth Almquist) from NetBSD.
92//config:
93//config:config ASH_BASH_COMPAT
94//config: bool "bash-compatible extensions"
95//config: default y
96//config: depends on ASH
97//config: help
98//config: Enable bash-compatible extensions.
99//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +0100100//config:config ASH_IDLE_TIMEOUT
101//config: bool "Idle timeout variable"
102//config: default n
103//config: depends on ASH
104//config: help
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100105//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko046341e2011-02-04 17:53:59 +0100106//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200107//config:config ASH_JOB_CONTROL
108//config: bool "Job control"
109//config: default y
110//config: depends on ASH
111//config: help
112//config: Enable job control in the ash shell.
113//config:
114//config:config ASH_ALIAS
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100115//config: bool "Alias support"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200116//config: default y
117//config: depends on ASH
118//config: help
119//config: Enable alias support in the ash shell.
120//config:
121//config:config ASH_GETOPTS
122//config: bool "Builtin getopt to parse positional parameters"
123//config: default y
124//config: depends on ASH
125//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100126//config: Enable support for getopts builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200127//config:
128//config:config ASH_BUILTIN_ECHO
129//config: bool "Builtin version of 'echo'"
130//config: default y
131//config: depends on ASH
132//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100133//config: Enable support for echo builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config:
135//config:config ASH_BUILTIN_PRINTF
136//config: bool "Builtin version of 'printf'"
137//config: default y
138//config: depends on ASH
139//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100140//config: Enable support for printf builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200141//config:
142//config:config ASH_BUILTIN_TEST
143//config: bool "Builtin version of 'test'"
144//config: default y
145//config: depends on ASH
146//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100147//config: Enable support for test builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200148//config:
149//config:config ASH_CMDCMD
150//config: bool "'command' command to override shell builtins"
151//config: default y
152//config: depends on ASH
153//config: help
154//config: Enable support for the ash 'command' builtin, which allows
155//config: you to run the specified command with the specified arguments,
156//config: even when there is an ash builtin command with the same name.
157//config:
158//config:config ASH_MAIL
159//config: bool "Check for new mail on interactive shells"
160//config: default n
161//config: depends on ASH
162//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100163//config: Enable "check for new mail" function in the ash shell.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200164//config:
165//config:config ASH_OPTIMIZE_FOR_SIZE
166//config: bool "Optimize for size instead of speed"
167//config: default y
168//config: depends on ASH
169//config: help
170//config: Compile ash for reduced size at the price of speed.
171//config:
172//config:config ASH_RANDOM_SUPPORT
173//config: bool "Pseudorandom generator and $RANDOM variable"
174//config: default y
175//config: depends on ASH
176//config: help
177//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
178//config: Each read of "$RANDOM" will generate a new pseudorandom value.
179//config: You can reset the generator by using a specified start value.
180//config: After "unset RANDOM" the generator will switch off and this
181//config: variable will no longer have special treatment.
182//config:
183//config:config ASH_EXPAND_PRMT
184//config: bool "Expand prompt string"
185//config: default y
186//config: depends on ASH
187//config: help
188//config: "PS#" may contain volatile content, such as backquote commands.
189//config: This option recreates the prompt string from the environment
190//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200191//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200192
Denis Vlasenkob012b102007-02-19 22:43:01 +0000193
Denis Vlasenko01631112007-12-16 17:20:38 +0000194/* ============ Hash table sizes. Configurable. */
195
196#define VTABSIZE 39
197#define ATABSIZE 39
198#define CMDTABLESIZE 31 /* should be prime */
199
200
Denis Vlasenkob012b102007-02-19 22:43:01 +0000201/* ============ Shell options */
202
203static const char *const optletters_optnames[] = {
204 "e" "errexit",
205 "f" "noglob",
206 "I" "ignoreeof",
207 "i" "interactive",
208 "m" "monitor",
209 "n" "noexec",
210 "s" "stdin",
211 "x" "xtrace",
212 "v" "verbose",
213 "C" "noclobber",
214 "a" "allexport",
215 "b" "notify",
216 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100217 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100218#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100219 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100220#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000221#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000222 ,"\0" "nolog"
223 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000224#endif
225};
226
Denys Vlasenko285ad152009-12-04 23:02:27 +0100227#define optletters(n) optletters_optnames[n][0]
228#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000229
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000230enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231
Eric Andersenc470f442003-07-28 09:56:35 +0000232
Denis Vlasenkob012b102007-02-19 22:43:01 +0000233/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000234
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200235#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000236
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000237/*
Eric Andersenc470f442003-07-28 09:56:35 +0000238 * We enclose jmp_buf in a structure so that we can declare pointers to
239 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000240 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000241 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000242 * exception handlers, the user should save the value of handler on entry
243 * to an inner scope, set handler to point to a jmploc structure for the
244 * inner scope, and restore handler on exit from the scope.
245 */
Eric Andersenc470f442003-07-28 09:56:35 +0000246struct jmploc {
247 jmp_buf loc;
248};
Denis Vlasenko01631112007-12-16 17:20:38 +0000249
250struct globals_misc {
251 /* pid of main shell */
252 int rootpid;
253 /* shell level: 0 for the main shell, 1 for its children, and so on */
254 int shlvl;
255#define rootshell (!shlvl)
256 char *minusc; /* argument to -c option */
257
258 char *curdir; // = nullstr; /* current working directory */
259 char *physdir; // = nullstr; /* physical working directory */
260
261 char *arg0; /* value of $0 */
262
263 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000264
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200265 volatile int suppress_int; /* counter */
266 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000267 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200268 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000269 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000270 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000271#define EXINT 0 /* SIGINT received */
272#define EXERROR 1 /* a generic error */
273#define EXSHELLPROC 2 /* execute a shell procedure */
274#define EXEXEC 3 /* command execution failed */
275#define EXEXIT 4 /* exit the shell */
276#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000277
Denis Vlasenko01631112007-12-16 17:20:38 +0000278 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000279 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000280
281 char optlist[NOPTS];
282#define eflag optlist[0]
283#define fflag optlist[1]
284#define Iflag optlist[2]
285#define iflag optlist[3]
286#define mflag optlist[4]
287#define nflag optlist[5]
288#define sflag optlist[6]
289#define xflag optlist[7]
290#define vflag optlist[8]
291#define Cflag optlist[9]
292#define aflag optlist[10]
293#define bflag optlist[11]
294#define uflag optlist[12]
295#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100296#if ENABLE_ASH_BASH_COMPAT
297# define pipefail optlist[14]
298#else
299# define pipefail 0
300#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000301#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100302# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
303# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000304#endif
305
306 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000307 /*
308 * Sigmode records the current value of the signal handlers for the various
309 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000310 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000311 */
312 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000313#define S_DFL 1 /* default signal handling (SIG_DFL) */
314#define S_CATCH 2 /* signal is caught */
315#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000316#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000317
Denis Vlasenko01631112007-12-16 17:20:38 +0000318 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000319 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200320 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000321 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200322 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000323
324 /* Rarely referenced stuff */
325#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200326 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000327#endif
328 pid_t backgndpid; /* pid of last background process */
329 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000330};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000331extern struct globals_misc *const ash_ptr_to_globals_misc;
332#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000333#define rootpid (G_misc.rootpid )
334#define shlvl (G_misc.shlvl )
335#define minusc (G_misc.minusc )
336#define curdir (G_misc.curdir )
337#define physdir (G_misc.physdir )
338#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000339#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000340#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200341#define suppress_int (G_misc.suppress_int )
342#define pending_int (G_misc.pending_int )
343#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000344#define isloginsh (G_misc.isloginsh )
345#define nullstr (G_misc.nullstr )
346#define optlist (G_misc.optlist )
347#define sigmode (G_misc.sigmode )
348#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200349#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000350#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200351#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200352#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000353#define backgndpid (G_misc.backgndpid )
354#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000355#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000356 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
357 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000358 curdir = nullstr; \
359 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200360 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000361} while (0)
362
363
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000364/* ============ DEBUG */
365#if DEBUG
366static void trace_printf(const char *fmt, ...);
367static void trace_vprintf(const char *fmt, va_list va);
368# define TRACE(param) trace_printf param
369# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000370# define close(fd) do { \
371 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000372 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200373 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000374 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000375} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000376#else
377# define TRACE(param)
378# define TRACEV(param)
379#endif
380
381
Denis Vlasenko559691a2008-10-05 18:39:31 +0000382/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000383#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
384
Denis Vlasenko559691a2008-10-05 18:39:31 +0000385static int isdigit_str9(const char *str)
386{
387 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
388 while (--maxlen && isdigit(*str))
389 str++;
390 return (*str == '\0');
391}
Denis Vlasenko01631112007-12-16 17:20:38 +0000392
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200393static const char *var_end(const char *var)
394{
395 while (*var)
396 if (*var++ == '=')
397 break;
398 return var;
399}
400
Denis Vlasenko559691a2008-10-05 18:39:31 +0000401
402/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100403
404static void exitshell(void) NORETURN;
405
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000406/*
Eric Andersen2870d962001-07-02 17:27:21 +0000407 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000408 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000409 * much more efficient and portable. (But hacking the kernel is so much
410 * more fun than worrying about efficiency and portability. :-))
411 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000412#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200413 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000414 xbarrier(); \
415} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000416
417/*
418 * Called to raise an exception. Since C doesn't include exceptions, we
419 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000420 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000421 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000422static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000423static void
424raise_exception(int e)
425{
426#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000427 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000428 abort();
429#endif
430 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000431 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000432 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000433}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000434#if DEBUG
435#define raise_exception(e) do { \
436 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
437 raise_exception(e); \
438} while (0)
439#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000440
441/*
442 * Called from trap.c when a SIGINT is received. (If the user specifies
443 * that SIGINT is to be trapped or ignored using the trap builtin, then
444 * this routine is not called.) Suppressint is nonzero when interrupts
445 * are held using the INT_OFF macro. (The test for iflag is just
446 * defensive programming.)
447 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000448static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000449static void
450raise_interrupt(void)
451{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000452 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000453
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200454 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000455 /* Signal is not automatically unmasked after it is raised,
456 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000457 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200458 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000459
Denis Vlasenko4b875702009-03-19 13:30:04 +0000460 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000461 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
462 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000463 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000464 signal(SIGINT, SIG_DFL);
465 raise(SIGINT);
466 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000467 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000468 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000469 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000470 /* NOTREACHED */
471}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000472#if DEBUG
473#define raise_interrupt() do { \
474 TRACE(("raising interrupt on line %d\n", __LINE__)); \
475 raise_interrupt(); \
476} while (0)
477#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000478
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000479static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000480int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000481{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000482 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200483 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000484 raise_interrupt();
485 }
486}
487#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000488static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000489force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000490{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000491 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200492 suppress_int = 0;
493 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000494 raise_interrupt();
495}
496#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000497
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200498#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000499
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000500#define RESTORE_INT(v) do { \
501 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200502 suppress_int = (v); \
503 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000504 raise_interrupt(); \
505} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000506
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000507
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000508/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000509
Eric Andersenc470f442003-07-28 09:56:35 +0000510static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000511outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000512{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000513 INT_OFF;
514 fputs(p, file);
515 INT_ON;
516}
517
518static void
519flush_stdout_stderr(void)
520{
521 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100522 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000523 INT_ON;
524}
525
526static void
527outcslow(int c, FILE *dest)
528{
529 INT_OFF;
530 putc(c, dest);
531 fflush(dest);
532 INT_ON;
533}
534
535static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
536static int
537out1fmt(const char *fmt, ...)
538{
539 va_list ap;
540 int r;
541
542 INT_OFF;
543 va_start(ap, fmt);
544 r = vprintf(fmt, ap);
545 va_end(ap);
546 INT_ON;
547 return r;
548}
549
550static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
551static int
552fmtstr(char *outbuf, size_t length, const char *fmt, ...)
553{
554 va_list ap;
555 int ret;
556
557 va_start(ap, fmt);
558 INT_OFF;
559 ret = vsnprintf(outbuf, length, fmt, ap);
560 va_end(ap);
561 INT_ON;
562 return ret;
563}
564
565static void
566out1str(const char *p)
567{
568 outstr(p, stdout);
569}
570
571static void
572out2str(const char *p)
573{
574 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100575 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000576}
577
578
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000579/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000580
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000581/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100582#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200583#define CTLESC ((unsigned char)'\201') /* escape next character */
584#define CTLVAR ((unsigned char)'\202') /* variable defn */
585#define CTLENDVAR ((unsigned char)'\203')
586#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000587#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
588/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200589#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
590#define CTLENDARI ((unsigned char)'\207')
591#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100592#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000593
594/* variable substitution byte (follows CTLVAR) */
595#define VSTYPE 0x0f /* type of variable substitution */
596#define VSNUL 0x10 /* colon--treat the empty string as unset */
597#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
598
599/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000600#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
601#define VSMINUS 0x2 /* ${var-text} */
602#define VSPLUS 0x3 /* ${var+text} */
603#define VSQUESTION 0x4 /* ${var?message} */
604#define VSASSIGN 0x5 /* ${var=text} */
605#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
606#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
607#define VSTRIMLEFT 0x8 /* ${var#pattern} */
608#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
609#define VSLENGTH 0xa /* ${#var} */
610#if ENABLE_ASH_BASH_COMPAT
611#define VSSUBSTR 0xc /* ${var:position:length} */
612#define VSREPLACE 0xd /* ${var/pattern/replacement} */
613#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
614#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000615
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000616static const char dolatstr[] ALIGN1 = {
617 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
618};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000619
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620#define NCMD 0
621#define NPIPE 1
622#define NREDIR 2
623#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000624#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000625#define NAND 5
626#define NOR 6
627#define NSEMI 7
628#define NIF 8
629#define NWHILE 9
630#define NUNTIL 10
631#define NFOR 11
632#define NCASE 12
633#define NCLIST 13
634#define NDEFUN 14
635#define NARG 15
636#define NTO 16
637#if ENABLE_ASH_BASH_COMPAT
638#define NTO2 17
639#endif
640#define NCLOBBER 18
641#define NFROM 19
642#define NFROMTO 20
643#define NAPPEND 21
644#define NTOFD 22
645#define NFROMFD 23
646#define NHERE 24
647#define NXHERE 25
648#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000649#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000650
651union node;
652
653struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000654 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000655 union node *assign;
656 union node *args;
657 union node *redirect;
658};
659
660struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000661 smallint type;
662 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000663 struct nodelist *cmdlist;
664};
665
666struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000667 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000668 union node *n;
669 union node *redirect;
670};
671
672struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000673 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000674 union node *ch1;
675 union node *ch2;
676};
677
678struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000679 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000680 union node *test;
681 union node *ifpart;
682 union node *elsepart;
683};
684
685struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000686 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000687 union node *args;
688 union node *body;
689 char *var;
690};
691
692struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000693 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000694 union node *expr;
695 union node *cases;
696};
697
698struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000699 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000700 union node *next;
701 union node *pattern;
702 union node *body;
703};
704
705struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000706 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000707 union node *next;
708 char *text;
709 struct nodelist *backquote;
710};
711
Denis Vlasenko559691a2008-10-05 18:39:31 +0000712/* nfile and ndup layout must match!
713 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
714 * that it is actually NTO2 (>&file), and change its type.
715 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000716struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000717 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000718 union node *next;
719 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000720 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000721 union node *fname;
722 char *expfname;
723};
724
725struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000726 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000727 union node *next;
728 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000729 int dupfd;
730 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000731 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000732};
733
734struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000735 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000736 union node *next;
737 int fd;
738 union node *doc;
739};
740
741struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000742 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000743 union node *com;
744};
745
746union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000747 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000748 struct ncmd ncmd;
749 struct npipe npipe;
750 struct nredir nredir;
751 struct nbinary nbinary;
752 struct nif nif;
753 struct nfor nfor;
754 struct ncase ncase;
755 struct nclist nclist;
756 struct narg narg;
757 struct nfile nfile;
758 struct ndup ndup;
759 struct nhere nhere;
760 struct nnot nnot;
761};
762
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200763/*
764 * NODE_EOF is returned by parsecmd when it encounters an end of file.
765 * It must be distinct from NULL.
766 */
767#define NODE_EOF ((union node *) -1L)
768
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000769struct nodelist {
770 struct nodelist *next;
771 union node *n;
772};
773
774struct funcnode {
775 int count;
776 union node n;
777};
778
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000779/*
780 * Free a parse tree.
781 */
782static void
783freefunc(struct funcnode *f)
784{
785 if (f && --f->count < 0)
786 free(f);
787}
788
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000789
790/* ============ Debugging output */
791
792#if DEBUG
793
794static FILE *tracefile;
795
796static void
797trace_printf(const char *fmt, ...)
798{
799 va_list va;
800
801 if (debug != 1)
802 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000803 if (DEBUG_TIME)
804 fprintf(tracefile, "%u ", (int) time(NULL));
805 if (DEBUG_PID)
806 fprintf(tracefile, "[%u] ", (int) getpid());
807 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200808 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000809 va_start(va, fmt);
810 vfprintf(tracefile, fmt, va);
811 va_end(va);
812}
813
814static void
815trace_vprintf(const char *fmt, va_list va)
816{
817 if (debug != 1)
818 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000819 if (DEBUG_TIME)
820 fprintf(tracefile, "%u ", (int) time(NULL));
821 if (DEBUG_PID)
822 fprintf(tracefile, "[%u] ", (int) getpid());
823 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200824 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000825 vfprintf(tracefile, fmt, va);
826}
827
828static void
829trace_puts(const char *s)
830{
831 if (debug != 1)
832 return;
833 fputs(s, tracefile);
834}
835
836static void
837trace_puts_quoted(char *s)
838{
839 char *p;
840 char c;
841
842 if (debug != 1)
843 return;
844 putc('"', tracefile);
845 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100846 switch ((unsigned char)*p) {
847 case '\n': c = 'n'; goto backslash;
848 case '\t': c = 't'; goto backslash;
849 case '\r': c = 'r'; goto backslash;
850 case '\"': c = '\"'; goto backslash;
851 case '\\': c = '\\'; goto backslash;
852 case CTLESC: c = 'e'; goto backslash;
853 case CTLVAR: c = 'v'; goto backslash;
854 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
855 case CTLBACKQ: c = 'q'; goto backslash;
856 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000857 backslash:
858 putc('\\', tracefile);
859 putc(c, tracefile);
860 break;
861 default:
862 if (*p >= ' ' && *p <= '~')
863 putc(*p, tracefile);
864 else {
865 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100866 putc((*p >> 6) & 03, tracefile);
867 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000868 putc(*p & 07, tracefile);
869 }
870 break;
871 }
872 }
873 putc('"', tracefile);
874}
875
876static void
877trace_puts_args(char **ap)
878{
879 if (debug != 1)
880 return;
881 if (!*ap)
882 return;
883 while (1) {
884 trace_puts_quoted(*ap);
885 if (!*++ap) {
886 putc('\n', tracefile);
887 break;
888 }
889 putc(' ', tracefile);
890 }
891}
892
893static void
894opentrace(void)
895{
896 char s[100];
897#ifdef O_APPEND
898 int flags;
899#endif
900
901 if (debug != 1) {
902 if (tracefile)
903 fflush(tracefile);
904 /* leave open because libedit might be using it */
905 return;
906 }
907 strcpy(s, "./trace");
908 if (tracefile) {
909 if (!freopen(s, "a", tracefile)) {
910 fprintf(stderr, "Can't re-open %s\n", s);
911 debug = 0;
912 return;
913 }
914 } else {
915 tracefile = fopen(s, "a");
916 if (tracefile == NULL) {
917 fprintf(stderr, "Can't open %s\n", s);
918 debug = 0;
919 return;
920 }
921 }
922#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000923 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000924 if (flags >= 0)
925 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
926#endif
927 setlinebuf(tracefile);
928 fputs("\nTracing started.\n", tracefile);
929}
930
931static void
932indent(int amount, char *pfx, FILE *fp)
933{
934 int i;
935
936 for (i = 0; i < amount; i++) {
937 if (pfx && i == amount - 1)
938 fputs(pfx, fp);
939 putc('\t', fp);
940 }
941}
942
943/* little circular references here... */
944static void shtree(union node *n, int ind, char *pfx, FILE *fp);
945
946static void
947sharg(union node *arg, FILE *fp)
948{
949 char *p;
950 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100951 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000952
953 if (arg->type != NARG) {
954 out1fmt("<node type %d>\n", arg->type);
955 abort();
956 }
957 bqlist = arg->narg.backquote;
958 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100959 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000960 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700961 p++;
962 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000963 break;
964 case CTLVAR:
965 putc('$', fp);
966 putc('{', fp);
967 subtype = *++p;
968 if (subtype == VSLENGTH)
969 putc('#', fp);
970
Dan Fandrich77d48722010-09-07 23:38:28 -0700971 while (*p != '=') {
972 putc(*p, fp);
973 p++;
974 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000975
976 if (subtype & VSNUL)
977 putc(':', fp);
978
979 switch (subtype & VSTYPE) {
980 case VSNORMAL:
981 putc('}', fp);
982 break;
983 case VSMINUS:
984 putc('-', fp);
985 break;
986 case VSPLUS:
987 putc('+', fp);
988 break;
989 case VSQUESTION:
990 putc('?', fp);
991 break;
992 case VSASSIGN:
993 putc('=', fp);
994 break;
995 case VSTRIMLEFT:
996 putc('#', fp);
997 break;
998 case VSTRIMLEFTMAX:
999 putc('#', fp);
1000 putc('#', fp);
1001 break;
1002 case VSTRIMRIGHT:
1003 putc('%', fp);
1004 break;
1005 case VSTRIMRIGHTMAX:
1006 putc('%', fp);
1007 putc('%', fp);
1008 break;
1009 case VSLENGTH:
1010 break;
1011 default:
1012 out1fmt("<subtype %d>", subtype);
1013 }
1014 break;
1015 case CTLENDVAR:
1016 putc('}', fp);
1017 break;
1018 case CTLBACKQ:
1019 case CTLBACKQ|CTLQUOTE:
1020 putc('$', fp);
1021 putc('(', fp);
1022 shtree(bqlist->n, -1, NULL, fp);
1023 putc(')', fp);
1024 break;
1025 default:
1026 putc(*p, fp);
1027 break;
1028 }
1029 }
1030}
1031
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001032static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001033shcmd(union node *cmd, FILE *fp)
1034{
1035 union node *np;
1036 int first;
1037 const char *s;
1038 int dftfd;
1039
1040 first = 1;
1041 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001042 if (!first)
1043 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001044 sharg(np, fp);
1045 first = 0;
1046 }
1047 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001048 if (!first)
1049 putc(' ', fp);
1050 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001051 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001052 case NTO: s = ">>"+1; dftfd = 1; break;
1053 case NCLOBBER: s = ">|"; dftfd = 1; break;
1054 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001055#if ENABLE_ASH_BASH_COMPAT
1056 case NTO2:
1057#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001058 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001059 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001060 case NFROMFD: s = "<&"; break;
1061 case NFROMTO: s = "<>"; break;
1062 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001063 }
1064 if (np->nfile.fd != dftfd)
1065 fprintf(fp, "%d", np->nfile.fd);
1066 fputs(s, fp);
1067 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1068 fprintf(fp, "%d", np->ndup.dupfd);
1069 } else {
1070 sharg(np->nfile.fname, fp);
1071 }
1072 first = 0;
1073 }
1074}
1075
1076static void
1077shtree(union node *n, int ind, char *pfx, FILE *fp)
1078{
1079 struct nodelist *lp;
1080 const char *s;
1081
1082 if (n == NULL)
1083 return;
1084
1085 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001086
1087 if (n == NODE_EOF) {
1088 fputs("<EOF>", fp);
1089 return;
1090 }
1091
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001092 switch (n->type) {
1093 case NSEMI:
1094 s = "; ";
1095 goto binop;
1096 case NAND:
1097 s = " && ";
1098 goto binop;
1099 case NOR:
1100 s = " || ";
1101 binop:
1102 shtree(n->nbinary.ch1, ind, NULL, fp);
1103 /* if (ind < 0) */
1104 fputs(s, fp);
1105 shtree(n->nbinary.ch2, ind, NULL, fp);
1106 break;
1107 case NCMD:
1108 shcmd(n, fp);
1109 if (ind >= 0)
1110 putc('\n', fp);
1111 break;
1112 case NPIPE:
1113 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001114 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001115 if (lp->next)
1116 fputs(" | ", fp);
1117 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001118 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001119 fputs(" &", fp);
1120 if (ind >= 0)
1121 putc('\n', fp);
1122 break;
1123 default:
1124 fprintf(fp, "<node type %d>", n->type);
1125 if (ind >= 0)
1126 putc('\n', fp);
1127 break;
1128 }
1129}
1130
1131static void
1132showtree(union node *n)
1133{
1134 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001135 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001136}
1137
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001138#endif /* DEBUG */
1139
1140
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001141/* ============ Parser data */
1142
1143/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001144 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1145 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001146struct strlist {
1147 struct strlist *next;
1148 char *text;
1149};
1150
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001151struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001152
Denis Vlasenkob012b102007-02-19 22:43:01 +00001153struct strpush {
1154 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001155 char *prev_string;
1156 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001157#if ENABLE_ASH_ALIAS
1158 struct alias *ap; /* if push was associated with an alias */
1159#endif
1160 char *string; /* remember the string since it may change */
1161};
1162
1163struct parsefile {
1164 struct parsefile *prev; /* preceding file on stack */
1165 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001166 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001167 int left_in_line; /* number of chars left in this line */
1168 int left_in_buffer; /* number of chars left in this buffer past the line */
1169 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001170 char *buf; /* input buffer */
1171 struct strpush *strpush; /* for pushing strings at this level */
1172 struct strpush basestrpush; /* so pushing one is fast */
1173};
1174
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001175static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001176static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001177static int startlinno; /* line # where last token started */
1178static char *commandname; /* currently executing command */
1179static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001180static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001181
1182
1183/* ============ Message printing */
1184
1185static void
1186ash_vmsg(const char *msg, va_list ap)
1187{
1188 fprintf(stderr, "%s: ", arg0);
1189 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001190 if (strcmp(arg0, commandname))
1191 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001192 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001193 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001194 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001195 vfprintf(stderr, msg, ap);
1196 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001197}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001198
1199/*
1200 * Exverror is called to raise the error exception. If the second argument
1201 * is not NULL then error prints an error message using printf style
1202 * formatting. It then raises the error exception.
1203 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001204static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001205static void
1206ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001207{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001208#if DEBUG
1209 if (msg) {
1210 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1211 TRACEV((msg, ap));
1212 TRACE(("\") pid=%d\n", getpid()));
1213 } else
1214 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1215 if (msg)
1216#endif
1217 ash_vmsg(msg, ap);
1218
1219 flush_stdout_stderr();
1220 raise_exception(cond);
1221 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001222}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001223
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001224static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001225static void
1226ash_msg_and_raise_error(const char *msg, ...)
1227{
1228 va_list ap;
1229
1230 va_start(ap, msg);
1231 ash_vmsg_and_raise(EXERROR, msg, ap);
1232 /* NOTREACHED */
1233 va_end(ap);
1234}
1235
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001236static void raise_error_syntax(const char *) NORETURN;
1237static void
1238raise_error_syntax(const char *msg)
1239{
1240 ash_msg_and_raise_error("syntax error: %s", msg);
1241 /* NOTREACHED */
1242}
1243
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001244static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001245static void
1246ash_msg_and_raise(int cond, const char *msg, ...)
1247{
1248 va_list ap;
1249
1250 va_start(ap, msg);
1251 ash_vmsg_and_raise(cond, msg, ap);
1252 /* NOTREACHED */
1253 va_end(ap);
1254}
1255
1256/*
1257 * error/warning routines for external builtins
1258 */
1259static void
1260ash_msg(const char *fmt, ...)
1261{
1262 va_list ap;
1263
1264 va_start(ap, fmt);
1265 ash_vmsg(fmt, ap);
1266 va_end(ap);
1267}
1268
1269/*
1270 * Return a string describing an error. The returned string may be a
1271 * pointer to a static buffer that will be overwritten on the next call.
1272 * Action describes the operation that got the error.
1273 */
1274static const char *
1275errmsg(int e, const char *em)
1276{
1277 if (e == ENOENT || e == ENOTDIR) {
1278 return em;
1279 }
1280 return strerror(e);
1281}
1282
1283
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001284/* ============ Memory allocation */
1285
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001286#if 0
1287/* I consider these wrappers nearly useless:
1288 * ok, they return you to nearest exception handler, but
1289 * how much memory do you leak in the process, making
1290 * memory starvation worse?
1291 */
1292static void *
1293ckrealloc(void * p, size_t nbytes)
1294{
1295 p = realloc(p, nbytes);
1296 if (!p)
1297 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1298 return p;
1299}
1300
1301static void *
1302ckmalloc(size_t nbytes)
1303{
1304 return ckrealloc(NULL, nbytes);
1305}
1306
1307static void *
1308ckzalloc(size_t nbytes)
1309{
1310 return memset(ckmalloc(nbytes), 0, nbytes);
1311}
1312
1313static char *
1314ckstrdup(const char *s)
1315{
1316 char *p = strdup(s);
1317 if (!p)
1318 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1319 return p;
1320}
1321#else
1322/* Using bbox equivalents. They exit if out of memory */
1323# define ckrealloc xrealloc
1324# define ckmalloc xmalloc
1325# define ckzalloc xzalloc
1326# define ckstrdup xstrdup
1327#endif
1328
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001329/*
1330 * It appears that grabstackstr() will barf with such alignments
1331 * because stalloc() will return a string allocated in a new stackblock.
1332 */
1333#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1334enum {
1335 /* Most machines require the value returned from malloc to be aligned
1336 * in some way. The following macro will get this right
1337 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001338 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001339 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001340 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001341};
1342
1343struct stack_block {
1344 struct stack_block *prev;
1345 char space[MINSIZE];
1346};
1347
1348struct stackmark {
1349 struct stack_block *stackp;
1350 char *stacknxt;
1351 size_t stacknleft;
1352 struct stackmark *marknext;
1353};
1354
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001355
Denis Vlasenko01631112007-12-16 17:20:38 +00001356struct globals_memstack {
1357 struct stack_block *g_stackp; // = &stackbase;
1358 struct stackmark *markp;
1359 char *g_stacknxt; // = stackbase.space;
1360 char *sstrend; // = stackbase.space + MINSIZE;
1361 size_t g_stacknleft; // = MINSIZE;
1362 int herefd; // = -1;
1363 struct stack_block stackbase;
1364};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001365extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1366#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001367#define g_stackp (G_memstack.g_stackp )
1368#define markp (G_memstack.markp )
1369#define g_stacknxt (G_memstack.g_stacknxt )
1370#define sstrend (G_memstack.sstrend )
1371#define g_stacknleft (G_memstack.g_stacknleft)
1372#define herefd (G_memstack.herefd )
1373#define stackbase (G_memstack.stackbase )
1374#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001375 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1376 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001377 g_stackp = &stackbase; \
1378 g_stacknxt = stackbase.space; \
1379 g_stacknleft = MINSIZE; \
1380 sstrend = stackbase.space + MINSIZE; \
1381 herefd = -1; \
1382} while (0)
1383
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001384
Denis Vlasenko01631112007-12-16 17:20:38 +00001385#define stackblock() ((void *)g_stacknxt)
1386#define stackblocksize() g_stacknleft
1387
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001388/*
1389 * Parse trees for commands are allocated in lifo order, so we use a stack
1390 * to make this more efficient, and also to avoid all sorts of exception
1391 * handling code to handle interrupts in the middle of a parse.
1392 *
1393 * The size 504 was chosen because the Ultrix malloc handles that size
1394 * well.
1395 */
1396static void *
1397stalloc(size_t nbytes)
1398{
1399 char *p;
1400 size_t aligned;
1401
1402 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001403 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001404 size_t len;
1405 size_t blocksize;
1406 struct stack_block *sp;
1407
1408 blocksize = aligned;
1409 if (blocksize < MINSIZE)
1410 blocksize = MINSIZE;
1411 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1412 if (len < blocksize)
1413 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1414 INT_OFF;
1415 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001416 sp->prev = g_stackp;
1417 g_stacknxt = sp->space;
1418 g_stacknleft = blocksize;
1419 sstrend = g_stacknxt + blocksize;
1420 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001421 INT_ON;
1422 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001423 p = g_stacknxt;
1424 g_stacknxt += aligned;
1425 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001426 return p;
1427}
1428
Denis Vlasenko597906c2008-02-20 16:38:54 +00001429static void *
1430stzalloc(size_t nbytes)
1431{
1432 return memset(stalloc(nbytes), 0, nbytes);
1433}
1434
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001435static void
1436stunalloc(void *p)
1437{
1438#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001439 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001440 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001441 abort();
1442 }
1443#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001444 g_stacknleft += g_stacknxt - (char *)p;
1445 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001446}
1447
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001448/*
1449 * Like strdup but works with the ash stack.
1450 */
1451static char *
1452ststrdup(const char *p)
1453{
1454 size_t len = strlen(p) + 1;
1455 return memcpy(stalloc(len), p, len);
1456}
1457
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001458static void
1459setstackmark(struct stackmark *mark)
1460{
Denis Vlasenko01631112007-12-16 17:20:38 +00001461 mark->stackp = g_stackp;
1462 mark->stacknxt = g_stacknxt;
1463 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001464 mark->marknext = markp;
1465 markp = mark;
1466}
1467
1468static void
1469popstackmark(struct stackmark *mark)
1470{
1471 struct stack_block *sp;
1472
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001473 if (!mark->stackp)
1474 return;
1475
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001476 INT_OFF;
1477 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001478 while (g_stackp != mark->stackp) {
1479 sp = g_stackp;
1480 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001481 free(sp);
1482 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001483 g_stacknxt = mark->stacknxt;
1484 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001485 sstrend = mark->stacknxt + mark->stacknleft;
1486 INT_ON;
1487}
1488
1489/*
1490 * When the parser reads in a string, it wants to stick the string on the
1491 * stack and only adjust the stack pointer when it knows how big the
1492 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1493 * of space on top of the stack and stackblocklen returns the length of
1494 * this block. Growstackblock will grow this space by at least one byte,
1495 * possibly moving it (like realloc). Grabstackblock actually allocates the
1496 * part of the block that has been used.
1497 */
1498static void
1499growstackblock(void)
1500{
1501 size_t newlen;
1502
Denis Vlasenko01631112007-12-16 17:20:38 +00001503 newlen = g_stacknleft * 2;
1504 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001505 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1506 if (newlen < 128)
1507 newlen += 128;
1508
Denis Vlasenko01631112007-12-16 17:20:38 +00001509 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001510 struct stack_block *oldstackp;
1511 struct stackmark *xmark;
1512 struct stack_block *sp;
1513 struct stack_block *prevstackp;
1514 size_t grosslen;
1515
1516 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001517 oldstackp = g_stackp;
1518 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001519 prevstackp = sp->prev;
1520 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1521 sp = ckrealloc(sp, grosslen);
1522 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001523 g_stackp = sp;
1524 g_stacknxt = sp->space;
1525 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001526 sstrend = sp->space + newlen;
1527
1528 /*
1529 * Stack marks pointing to the start of the old block
1530 * must be relocated to point to the new block
1531 */
1532 xmark = markp;
1533 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001534 xmark->stackp = g_stackp;
1535 xmark->stacknxt = g_stacknxt;
1536 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001537 xmark = xmark->marknext;
1538 }
1539 INT_ON;
1540 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001541 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001542 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001543 char *p = stalloc(newlen);
1544
1545 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001546 g_stacknxt = memcpy(p, oldspace, oldlen);
1547 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001548 }
1549}
1550
1551static void
1552grabstackblock(size_t len)
1553{
1554 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001555 g_stacknxt += len;
1556 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001557}
1558
1559/*
1560 * The following routines are somewhat easier to use than the above.
1561 * The user declares a variable of type STACKSTR, which may be declared
1562 * to be a register. The macro STARTSTACKSTR initializes things. Then
1563 * the user uses the macro STPUTC to add characters to the string. In
1564 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1565 * grown as necessary. When the user is done, she can just leave the
1566 * string there and refer to it using stackblock(). Or she can allocate
1567 * the space for it using grabstackstr(). If it is necessary to allow
1568 * someone else to use the stack temporarily and then continue to grow
1569 * the string, the user should use grabstack to allocate the space, and
1570 * then call ungrabstr(p) to return to the previous mode of operation.
1571 *
1572 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1573 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1574 * is space for at least one character.
1575 */
1576static void *
1577growstackstr(void)
1578{
1579 size_t len = stackblocksize();
1580 if (herefd >= 0 && len >= 1024) {
1581 full_write(herefd, stackblock(), len);
1582 return stackblock();
1583 }
1584 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001585 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001586}
1587
1588/*
1589 * Called from CHECKSTRSPACE.
1590 */
1591static char *
1592makestrspace(size_t newlen, char *p)
1593{
Denis Vlasenko01631112007-12-16 17:20:38 +00001594 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001595 size_t size = stackblocksize();
1596
1597 for (;;) {
1598 size_t nleft;
1599
1600 size = stackblocksize();
1601 nleft = size - len;
1602 if (nleft >= newlen)
1603 break;
1604 growstackblock();
1605 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001606 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001607}
1608
1609static char *
1610stack_nputstr(const char *s, size_t n, char *p)
1611{
1612 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001613 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001614 return p;
1615}
1616
1617static char *
1618stack_putstr(const char *s, char *p)
1619{
1620 return stack_nputstr(s, strlen(s), p);
1621}
1622
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001623static char *
1624_STPUTC(int c, char *p)
1625{
1626 if (p == sstrend)
1627 p = growstackstr();
1628 *p++ = c;
1629 return p;
1630}
1631
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001632#define STARTSTACKSTR(p) ((p) = stackblock())
1633#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001634#define CHECKSTRSPACE(n, p) do { \
1635 char *q = (p); \
1636 size_t l = (n); \
1637 size_t m = sstrend - q; \
1638 if (l > m) \
1639 (p) = makestrspace(l, q); \
1640} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001641#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001642#define STACKSTRNUL(p) do { \
1643 if ((p) == sstrend) \
1644 (p) = growstackstr(); \
1645 *(p) = '\0'; \
1646} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001647#define STUNPUTC(p) (--(p))
1648#define STTOPC(p) ((p)[-1])
1649#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001650
1651#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001652#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001653#define stackstrend() ((void *)sstrend)
1654
1655
1656/* ============ String helpers */
1657
1658/*
1659 * prefix -- see if pfx is a prefix of string.
1660 */
1661static char *
1662prefix(const char *string, const char *pfx)
1663{
1664 while (*pfx) {
1665 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001666 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001667 }
1668 return (char *) string;
1669}
1670
1671/*
1672 * Check for a valid number. This should be elsewhere.
1673 */
1674static int
1675is_number(const char *p)
1676{
1677 do {
1678 if (!isdigit(*p))
1679 return 0;
1680 } while (*++p != '\0');
1681 return 1;
1682}
1683
1684/*
1685 * Convert a string of digits to an integer, printing an error message on
1686 * failure.
1687 */
1688static int
1689number(const char *s)
1690{
1691 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001692 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001693 return atoi(s);
1694}
1695
1696/*
1697 * Produce a possibly single quoted string suitable as input to the shell.
1698 * The return string is allocated on the stack.
1699 */
1700static char *
1701single_quote(const char *s)
1702{
1703 char *p;
1704
1705 STARTSTACKSTR(p);
1706
1707 do {
1708 char *q;
1709 size_t len;
1710
1711 len = strchrnul(s, '\'') - s;
1712
1713 q = p = makestrspace(len + 3, p);
1714
1715 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001716 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001717 *q++ = '\'';
1718 s += len;
1719
1720 STADJUST(q - p, p);
1721
Denys Vlasenkocd716832009-11-28 22:14:02 +01001722 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001724 len = 0;
1725 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726
1727 q = p = makestrspace(len + 3, p);
1728
1729 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001730 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732
1733 STADJUST(q - p, p);
1734 } while (*s);
1735
Denys Vlasenkocd716832009-11-28 22:14:02 +01001736 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001737
1738 return stackblock();
1739}
1740
1741
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001742/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743
1744static char **argptr; /* argument list for builtin commands */
1745static char *optionarg; /* set by nextopt (like getopt) */
1746static char *optptr; /* used by nextopt */
1747
1748/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001749 * XXX - should get rid of. Have all builtins use getopt(3).
1750 * The library getopt must have the BSD extension static variable
1751 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001752 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001753 * Standard option processing (a la getopt) for builtin routines.
1754 * The only argument that is passed to nextopt is the option string;
1755 * the other arguments are unnecessary. It returns the character,
1756 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757 */
1758static int
1759nextopt(const char *optstring)
1760{
1761 char *p;
1762 const char *q;
1763 char c;
1764
1765 p = optptr;
1766 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001767 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001768 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001769 if (p == NULL)
1770 return '\0';
1771 if (*p != '-')
1772 return '\0';
1773 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774 return '\0';
1775 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001776 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001777 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001778 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001779 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001780 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001781 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001782 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001784 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785 if (*++q == ':')
1786 q++;
1787 }
1788 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001789 if (*p == '\0') {
1790 p = *argptr++;
1791 if (p == NULL)
1792 ash_msg_and_raise_error("no arg for -%c option", c);
1793 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001794 optionarg = p;
1795 p = NULL;
1796 }
1797 optptr = p;
1798 return c;
1799}
1800
1801
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001802/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001803
Denis Vlasenko01631112007-12-16 17:20:38 +00001804/*
1805 * The parsefile structure pointed to by the global variable parsefile
1806 * contains information about the current file being read.
1807 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001808struct shparam {
1809 int nparam; /* # of positional parameters (without $0) */
1810#if ENABLE_ASH_GETOPTS
1811 int optind; /* next parameter to be processed by getopts */
1812 int optoff; /* used by getopts */
1813#endif
1814 unsigned char malloced; /* if parameter list dynamically allocated */
1815 char **p; /* parameter list */
1816};
1817
1818/*
1819 * Free the list of positional parameters.
1820 */
1821static void
1822freeparam(volatile struct shparam *param)
1823{
Denis Vlasenko01631112007-12-16 17:20:38 +00001824 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001825 char **ap, **ap1;
1826 ap = ap1 = param->p;
1827 while (*ap)
1828 free(*ap++);
1829 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001830 }
1831}
1832
1833#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001834static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001835#endif
1836
1837struct var {
1838 struct var *next; /* next entry in hash list */
1839 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001840 const char *var_text; /* name=value */
1841 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001842 /* the variable gets set/unset */
1843};
1844
1845struct localvar {
1846 struct localvar *next; /* next local variable in list */
1847 struct var *vp; /* the variable that was made local */
1848 int flags; /* saved flags */
1849 const char *text; /* saved text */
1850};
1851
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001852/* flags */
1853#define VEXPORT 0x01 /* variable is exported */
1854#define VREADONLY 0x02 /* variable cannot be modified */
1855#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1856#define VTEXTFIXED 0x08 /* text is statically allocated */
1857#define VSTACK 0x10 /* text is allocated on the stack */
1858#define VUNSET 0x20 /* the variable is not set */
1859#define VNOFUNC 0x40 /* don't call the callback function */
1860#define VNOSET 0x80 /* do not set variable - just readonly test */
1861#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001862#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001863# define VDYNAMIC 0x200 /* dynamic variable */
1864#else
1865# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001866#endif
1867
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001868
Denis Vlasenko01631112007-12-16 17:20:38 +00001869/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001870#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001871static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001872change_lc_all(const char *value)
1873{
1874 if (value && *value != '\0')
1875 setlocale(LC_ALL, value);
1876}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001877static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001878change_lc_ctype(const char *value)
1879{
1880 if (value && *value != '\0')
1881 setlocale(LC_CTYPE, value);
1882}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001883#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001884#if ENABLE_ASH_MAIL
1885static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001886static void changemail(const char *var_value) FAST_FUNC;
1887#else
1888# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001889#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001890static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001892static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001893#endif
1894
Denis Vlasenko01631112007-12-16 17:20:38 +00001895static const struct {
1896 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001897 const char *var_text;
1898 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001899} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001900 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001901#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001902 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1903 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001904#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001905 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1906 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1907 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1908 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001909#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001910 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
1912#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001913 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001914#endif
1915#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001916 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1917 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001918#endif
1919#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001920 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001921#endif
1922};
1923
Denis Vlasenko0b769642008-07-24 07:54:57 +00001924struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001925
1926struct globals_var {
1927 struct shparam shellparam; /* $@ current positional parameters */
1928 struct redirtab *redirlist;
1929 int g_nullredirs;
1930 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1931 struct var *vartab[VTABSIZE];
1932 struct var varinit[ARRAY_SIZE(varinit_data)];
1933};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001934extern struct globals_var *const ash_ptr_to_globals_var;
1935#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001936#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001937//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001938#define g_nullredirs (G_var.g_nullredirs )
1939#define preverrout_fd (G_var.preverrout_fd)
1940#define vartab (G_var.vartab )
1941#define varinit (G_var.varinit )
1942#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001943 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001944 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1945 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001946 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001947 varinit[i].flags = varinit_data[i].flags; \
1948 varinit[i].var_text = varinit_data[i].var_text; \
1949 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001950 } \
1951} while (0)
1952
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001953#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001954#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001955# define vmail (&vifs)[1]
1956# define vmpath (&vmail)[1]
1957# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001958#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001959# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001960#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001961#define vps1 (&vpath)[1]
1962#define vps2 (&vps1)[1]
1963#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001965# define voptind (&vps4)[1]
1966# if ENABLE_ASH_RANDOM_SUPPORT
1967# define vrandom (&voptind)[1]
1968# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001969#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001970# if ENABLE_ASH_RANDOM_SUPPORT
1971# define vrandom (&vps4)[1]
1972# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001973#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001974
1975/*
1976 * The following macros access the values of the above variables.
1977 * They have to skip over the name. They return the null string
1978 * for unset variables.
1979 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001980#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001981#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001982#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001983# define mailval() (vmail.var_text + 5)
1984# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001985# define mpathset() ((vmpath.flags & VUNSET) == 0)
1986#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001987#define pathval() (vpath.var_text + 5)
1988#define ps1val() (vps1.var_text + 4)
1989#define ps2val() (vps2.var_text + 4)
1990#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001991#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001992# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001993#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001994
Denis Vlasenko01631112007-12-16 17:20:38 +00001995#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001996static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001997getoptsreset(const char *value)
1998{
1999 shellparam.optind = number(value);
2000 shellparam.optoff = -1;
2001}
2002#endif
2003
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002004/* math.h has these, otherwise define our private copies */
2005#if !ENABLE_SH_MATH_SUPPORT
2006#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2007#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002008/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002009 * Return the pointer to the first char which is not part of a legal variable name
2010 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002011 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002012static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002013endofname(const char *name)
2014{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002015 if (!is_name(*name))
2016 return name;
2017 while (*++name) {
2018 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002019 break;
2020 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002021 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002022}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002023#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002024
2025/*
2026 * Compares two strings up to the first = or '\0'. The first
2027 * string must be terminated by '='; the second may be terminated by
2028 * either '=' or '\0'.
2029 */
2030static int
2031varcmp(const char *p, const char *q)
2032{
2033 int c, d;
2034
2035 while ((c = *p) == (d = *q)) {
2036 if (!c || c == '=')
2037 goto out;
2038 p++;
2039 q++;
2040 }
2041 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002042 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002043 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002044 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002045 out:
2046 return c - d;
2047}
2048
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002049/*
2050 * Find the appropriate entry in the hash table from the name.
2051 */
2052static struct var **
2053hashvar(const char *p)
2054{
2055 unsigned hashval;
2056
2057 hashval = ((unsigned char) *p) << 4;
2058 while (*p && *p != '=')
2059 hashval += (unsigned char) *p++;
2060 return &vartab[hashval % VTABSIZE];
2061}
2062
2063static int
2064vpcmp(const void *a, const void *b)
2065{
2066 return varcmp(*(const char **)a, *(const char **)b);
2067}
2068
2069/*
2070 * This routine initializes the builtin variables.
2071 */
2072static void
2073initvar(void)
2074{
2075 struct var *vp;
2076 struct var *end;
2077 struct var **vpp;
2078
2079 /*
2080 * PS1 depends on uid
2081 */
2082#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002083 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002084#else
2085 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002086 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002087#endif
2088 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002089 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002090 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002091 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002092 vp->next = *vpp;
2093 *vpp = vp;
2094 } while (++vp < end);
2095}
2096
2097static struct var **
2098findvar(struct var **vpp, const char *name)
2099{
2100 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002101 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002102 break;
2103 }
2104 }
2105 return vpp;
2106}
2107
2108/*
2109 * Find the value of a variable. Returns NULL if not set.
2110 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002111static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112lookupvar(const char *name)
2113{
2114 struct var *v;
2115
2116 v = *findvar(hashvar(name), name);
2117 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002118#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119 /*
2120 * Dynamic variables are implemented roughly the same way they are
2121 * in bash. Namely, they're "special" so long as they aren't unset.
2122 * As soon as they're unset, they're no longer dynamic, and dynamic
2123 * lookup will no longer happen at that point. -- PFM.
2124 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002125 if (v->flags & VDYNAMIC)
2126 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002127#endif
2128 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002129 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002130 }
2131 return NULL;
2132}
2133
2134/*
2135 * Search the environment of a builtin command.
2136 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002137static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002138bltinlookup(const char *name)
2139{
2140 struct strlist *sp;
2141
2142 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002143 if (varcmp(sp->text, name) == 0)
2144 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002145 }
2146 return lookupvar(name);
2147}
2148
2149/*
2150 * Same as setvar except that the variable and value are passed in
2151 * the first argument as name=value. Since the first argument will
2152 * be actually stored in the table, it should not be a string that
2153 * will go away.
2154 * Called with interrupts off.
2155 */
2156static void
2157setvareq(char *s, int flags)
2158{
2159 struct var *vp, **vpp;
2160
2161 vpp = hashvar(s);
2162 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2163 vp = *findvar(vpp, s);
2164 if (vp) {
2165 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2166 const char *n;
2167
2168 if (flags & VNOSAVE)
2169 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002170 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002171 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2172 }
2173
2174 if (flags & VNOSET)
2175 return;
2176
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002177 if (vp->var_func && !(flags & VNOFUNC))
2178 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002179
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002180 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2181 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002182
2183 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2184 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002185 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186 if (flags & VNOSET)
2187 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002188 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002189 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002190 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002191 *vpp = vp;
2192 }
2193 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2194 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002195 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002196 vp->flags = flags;
2197}
2198
2199/*
2200 * Set the value of a variable. The flags argument is ored with the
2201 * flags of the variable. If val is NULL, the variable is unset.
2202 */
2203static void
2204setvar(const char *name, const char *val, int flags)
2205{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002206 const char *q;
2207 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002208 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002209 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002210 size_t vallen;
2211
2212 q = endofname(name);
2213 p = strchrnul(q, '=');
2214 namelen = p - name;
2215 if (!namelen || p != q)
2216 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2217 vallen = 0;
2218 if (val == NULL) {
2219 flags |= VUNSET;
2220 } else {
2221 vallen = strlen(val);
2222 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002223
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002224 INT_OFF;
2225 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002226 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002227 if (val) {
2228 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002229 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002230 }
2231 *p = '\0';
2232 setvareq(nameeq, flags | VNOSAVE);
2233 INT_ON;
2234}
2235
Denys Vlasenko03dad222010-01-12 23:29:57 +01002236static void FAST_FUNC
2237setvar2(const char *name, const char *val)
2238{
2239 setvar(name, val, 0);
2240}
2241
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002242#if ENABLE_ASH_GETOPTS
2243/*
2244 * Safe version of setvar, returns 1 on success 0 on failure.
2245 */
2246static int
2247setvarsafe(const char *name, const char *val, int flags)
2248{
2249 int err;
2250 volatile int saveint;
2251 struct jmploc *volatile savehandler = exception_handler;
2252 struct jmploc jmploc;
2253
2254 SAVE_INT(saveint);
2255 if (setjmp(jmploc.loc))
2256 err = 1;
2257 else {
2258 exception_handler = &jmploc;
2259 setvar(name, val, flags);
2260 err = 0;
2261 }
2262 exception_handler = savehandler;
2263 RESTORE_INT(saveint);
2264 return err;
2265}
2266#endif
2267
2268/*
2269 * Unset the specified variable.
2270 */
2271static int
2272unsetvar(const char *s)
2273{
2274 struct var **vpp;
2275 struct var *vp;
2276 int retval;
2277
2278 vpp = findvar(hashvar(s), s);
2279 vp = *vpp;
2280 retval = 2;
2281 if (vp) {
2282 int flags = vp->flags;
2283
2284 retval = 1;
2285 if (flags & VREADONLY)
2286 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002287#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002288 vp->flags &= ~VDYNAMIC;
2289#endif
2290 if (flags & VUNSET)
2291 goto ok;
2292 if ((flags & VSTRFIXED) == 0) {
2293 INT_OFF;
2294 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002295 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002296 *vpp = vp->next;
2297 free(vp);
2298 INT_ON;
2299 } else {
2300 setvar(s, 0, 0);
2301 vp->flags &= ~VEXPORT;
2302 }
2303 ok:
2304 retval = 0;
2305 }
2306 out:
2307 return retval;
2308}
2309
2310/*
2311 * Process a linked list of variable assignments.
2312 */
2313static void
2314listsetvar(struct strlist *list_set_var, int flags)
2315{
2316 struct strlist *lp = list_set_var;
2317
2318 if (!lp)
2319 return;
2320 INT_OFF;
2321 do {
2322 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002323 lp = lp->next;
2324 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002325 INT_ON;
2326}
2327
2328/*
2329 * Generate a list of variables satisfying the given conditions.
2330 */
2331static char **
2332listvars(int on, int off, char ***end)
2333{
2334 struct var **vpp;
2335 struct var *vp;
2336 char **ep;
2337 int mask;
2338
2339 STARTSTACKSTR(ep);
2340 vpp = vartab;
2341 mask = on | off;
2342 do {
2343 for (vp = *vpp; vp; vp = vp->next) {
2344 if ((vp->flags & mask) == on) {
2345 if (ep == stackstrend())
2346 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002347 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002348 }
2349 }
2350 } while (++vpp < vartab + VTABSIZE);
2351 if (ep == stackstrend())
2352 ep = growstackstr();
2353 if (end)
2354 *end = ep;
2355 *ep++ = NULL;
2356 return grabstackstr(ep);
2357}
2358
2359
2360/* ============ Path search helper
2361 *
2362 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002363 * of the path before the first call; path_advance will update
2364 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002365 * the possible path expansions in sequence. If an option (indicated by
2366 * a percent sign) appears in the path entry then the global variable
2367 * pathopt will be set to point to it; otherwise pathopt will be set to
2368 * NULL.
2369 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002370static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002371
2372static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002373path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002374{
2375 const char *p;
2376 char *q;
2377 const char *start;
2378 size_t len;
2379
2380 if (*path == NULL)
2381 return NULL;
2382 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002383 for (p = start; *p && *p != ':' && *p != '%'; p++)
2384 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002385 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2386 while (stackblocksize() < len)
2387 growstackblock();
2388 q = stackblock();
2389 if (p != start) {
2390 memcpy(q, start, p - start);
2391 q += p - start;
2392 *q++ = '/';
2393 }
2394 strcpy(q, name);
2395 pathopt = NULL;
2396 if (*p == '%') {
2397 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002398 while (*p && *p != ':')
2399 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002400 }
2401 if (*p == ':')
2402 *path = p + 1;
2403 else
2404 *path = NULL;
2405 return stalloc(len);
2406}
2407
2408
2409/* ============ Prompt */
2410
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002411static smallint doprompt; /* if set, prompt the user */
2412static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002413
2414#if ENABLE_FEATURE_EDITING
2415static line_input_t *line_input_state;
2416static const char *cmdedit_prompt;
2417static void
2418putprompt(const char *s)
2419{
2420 if (ENABLE_ASH_EXPAND_PRMT) {
2421 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002422 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002423 return;
2424 }
2425 cmdedit_prompt = s;
2426}
2427#else
2428static void
2429putprompt(const char *s)
2430{
2431 out2str(s);
2432}
2433#endif
2434
2435#if ENABLE_ASH_EXPAND_PRMT
2436/* expandstr() needs parsing machinery, so it is far away ahead... */
2437static const char *expandstr(const char *ps);
2438#else
2439#define expandstr(s) s
2440#endif
2441
2442static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002443setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002444{
2445 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002446 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2447
2448 if (!do_set)
2449 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002450
2451 needprompt = 0;
2452
2453 switch (whichprompt) {
2454 case 1:
2455 prompt = ps1val();
2456 break;
2457 case 2:
2458 prompt = ps2val();
2459 break;
2460 default: /* 0 */
2461 prompt = nullstr;
2462 }
2463#if ENABLE_ASH_EXPAND_PRMT
2464 setstackmark(&smark);
2465 stalloc(stackblocksize());
2466#endif
2467 putprompt(expandstr(prompt));
2468#if ENABLE_ASH_EXPAND_PRMT
2469 popstackmark(&smark);
2470#endif
2471}
2472
2473
2474/* ============ The cd and pwd commands */
2475
2476#define CD_PHYSICAL 1
2477#define CD_PRINT 2
2478
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002479static int
2480cdopt(void)
2481{
2482 int flags = 0;
2483 int i, j;
2484
2485 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002486 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002487 if (i != j) {
2488 flags ^= CD_PHYSICAL;
2489 j = i;
2490 }
2491 }
2492
2493 return flags;
2494}
2495
2496/*
2497 * Update curdir (the name of the current directory) in response to a
2498 * cd command.
2499 */
2500static const char *
2501updatepwd(const char *dir)
2502{
2503 char *new;
2504 char *p;
2505 char *cdcomppath;
2506 const char *lim;
2507
2508 cdcomppath = ststrdup(dir);
2509 STARTSTACKSTR(new);
2510 if (*dir != '/') {
2511 if (curdir == nullstr)
2512 return 0;
2513 new = stack_putstr(curdir, new);
2514 }
2515 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002516 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002517 if (*dir != '/') {
2518 if (new[-1] != '/')
2519 USTPUTC('/', new);
2520 if (new > lim && *lim == '/')
2521 lim++;
2522 } else {
2523 USTPUTC('/', new);
2524 cdcomppath++;
2525 if (dir[1] == '/' && dir[2] != '/') {
2526 USTPUTC('/', new);
2527 cdcomppath++;
2528 lim++;
2529 }
2530 }
2531 p = strtok(cdcomppath, "/");
2532 while (p) {
2533 switch (*p) {
2534 case '.':
2535 if (p[1] == '.' && p[2] == '\0') {
2536 while (new > lim) {
2537 STUNPUTC(new);
2538 if (new[-1] == '/')
2539 break;
2540 }
2541 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002542 }
2543 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002544 break;
2545 /* fall through */
2546 default:
2547 new = stack_putstr(p, new);
2548 USTPUTC('/', new);
2549 }
2550 p = strtok(0, "/");
2551 }
2552 if (new > lim)
2553 STUNPUTC(new);
2554 *new = 0;
2555 return stackblock();
2556}
2557
2558/*
2559 * Find out what the current directory is. If we already know the current
2560 * directory, this routine returns immediately.
2561 */
2562static char *
2563getpwd(void)
2564{
Denis Vlasenko01631112007-12-16 17:20:38 +00002565 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002566 return dir ? dir : nullstr;
2567}
2568
2569static void
2570setpwd(const char *val, int setold)
2571{
2572 char *oldcur, *dir;
2573
2574 oldcur = dir = curdir;
2575
2576 if (setold) {
2577 setvar("OLDPWD", oldcur, VEXPORT);
2578 }
2579 INT_OFF;
2580 if (physdir != nullstr) {
2581 if (physdir != oldcur)
2582 free(physdir);
2583 physdir = nullstr;
2584 }
2585 if (oldcur == val || !val) {
2586 char *s = getpwd();
2587 physdir = s;
2588 if (!val)
2589 dir = s;
2590 } else
2591 dir = ckstrdup(val);
2592 if (oldcur != dir && oldcur != nullstr) {
2593 free(oldcur);
2594 }
2595 curdir = dir;
2596 INT_ON;
2597 setvar("PWD", dir, VEXPORT);
2598}
2599
2600static void hashcd(void);
2601
2602/*
2603 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2604 * know that the current directory has changed.
2605 */
2606static int
2607docd(const char *dest, int flags)
2608{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002609 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002610 int err;
2611
2612 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2613
2614 INT_OFF;
2615 if (!(flags & CD_PHYSICAL)) {
2616 dir = updatepwd(dest);
2617 if (dir)
2618 dest = dir;
2619 }
2620 err = chdir(dest);
2621 if (err)
2622 goto out;
2623 setpwd(dir, 1);
2624 hashcd();
2625 out:
2626 INT_ON;
2627 return err;
2628}
2629
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002630static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002631cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002632{
2633 const char *dest;
2634 const char *path;
2635 const char *p;
2636 char c;
2637 struct stat statb;
2638 int flags;
2639
2640 flags = cdopt();
2641 dest = *argptr;
2642 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002643 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002644 else if (LONE_DASH(dest)) {
2645 dest = bltinlookup("OLDPWD");
2646 flags |= CD_PRINT;
2647 }
2648 if (!dest)
2649 dest = nullstr;
2650 if (*dest == '/')
2651 goto step7;
2652 if (*dest == '.') {
2653 c = dest[1];
2654 dotdot:
2655 switch (c) {
2656 case '\0':
2657 case '/':
2658 goto step6;
2659 case '.':
2660 c = dest[2];
2661 if (c != '.')
2662 goto dotdot;
2663 }
2664 }
2665 if (!*dest)
2666 dest = ".";
2667 path = bltinlookup("CDPATH");
2668 if (!path) {
2669 step6:
2670 step7:
2671 p = dest;
2672 goto docd;
2673 }
2674 do {
2675 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002676 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002677 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2678 if (c && c != ':')
2679 flags |= CD_PRINT;
2680 docd:
2681 if (!docd(p, flags))
2682 goto out;
2683 break;
2684 }
2685 } while (path);
2686 ash_msg_and_raise_error("can't cd to %s", dest);
2687 /* NOTREACHED */
2688 out:
2689 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002690 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002691 return 0;
2692}
2693
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002694static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002695pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002696{
2697 int flags;
2698 const char *dir = curdir;
2699
2700 flags = cdopt();
2701 if (flags) {
2702 if (physdir == nullstr)
2703 setpwd(dir, 0);
2704 dir = physdir;
2705 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002706 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002707 return 0;
2708}
2709
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002710
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002711/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002712
Denis Vlasenko834dee72008-10-07 09:18:30 +00002713
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002714#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002715
Eric Andersenc470f442003-07-28 09:56:35 +00002716/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002717#define CWORD 0 /* character is nothing special */
2718#define CNL 1 /* newline character */
2719#define CBACK 2 /* a backslash character */
2720#define CSQUOTE 3 /* single quote */
2721#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002722#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002723#define CBQUOTE 6 /* backwards single quote */
2724#define CVAR 7 /* a dollar sign */
2725#define CENDVAR 8 /* a '}' character */
2726#define CLP 9 /* a left paren in arithmetic */
2727#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002728#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002729#define CCTL 12 /* like CWORD, except it must be escaped */
2730#define CSPCL 13 /* these terminate a word */
2731#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002732
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002733#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002734#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002735# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002736#endif
2737
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002738#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002739
Mike Frysinger98c52642009-04-02 10:02:37 +00002740#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002741# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002742#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002743# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002744#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002745static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002746#if ENABLE_ASH_ALIAS
2747 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2748#endif
2749 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2750 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2751 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2752 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2753 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2754 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2755 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2756 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2757 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2758 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2759 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002760#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002761 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2762 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2763 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2764#endif
2765#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002766};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002767/* Constants below must match table above */
2768enum {
2769#if ENABLE_ASH_ALIAS
2770 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2771#endif
2772 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2773 CNL_CNL_CNL_CNL , /* 2 */
2774 CWORD_CCTL_CCTL_CWORD , /* 3 */
2775 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2776 CVAR_CVAR_CWORD_CVAR , /* 5 */
2777 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2778 CSPCL_CWORD_CWORD_CLP , /* 7 */
2779 CSPCL_CWORD_CWORD_CRP , /* 8 */
2780 CBACK_CBACK_CCTL_CBACK , /* 9 */
2781 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2782 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2783 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2784 CWORD_CWORD_CWORD_CWORD , /* 13 */
2785 CCTL_CCTL_CCTL_CCTL , /* 14 */
2786};
Eric Andersen2870d962001-07-02 17:27:21 +00002787
Denys Vlasenkocd716832009-11-28 22:14:02 +01002788/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2789 * caller must ensure proper cast on it if c is *char_ptr!
2790 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002791/* Values for syntax param */
2792#define BASESYNTAX 0 /* not in quotes */
2793#define DQSYNTAX 1 /* in double quotes */
2794#define SQSYNTAX 2 /* in single quotes */
2795#define ARISYNTAX 3 /* in arithmetic */
2796#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002797
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002798#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002799
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002800static int
2801SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002802{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002803 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002804# if ENABLE_ASH_ALIAS
2805 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002806 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2807 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2808 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2809 11, 3 /* "}~" */
2810 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002811# else
2812 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002813 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2814 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2815 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2816 10, 2 /* "}~" */
2817 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002818# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002819 const char *s;
2820 int indx;
2821
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002822 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002823 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002824# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002825 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002826 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002827 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002828# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002829 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002830 /* Cast is purely for paranoia here,
2831 * just in case someone passed signed char to us */
2832 if ((unsigned char)c >= CTL_FIRST
2833 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002834 ) {
2835 return CCTL;
2836 }
2837 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002838 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002839 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002840 indx = syntax_index_table[s - spec_symbls];
2841 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002842 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002843}
2844
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002845#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002846
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002847static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002848 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002849 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2859 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2860 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2882 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2883 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2884 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2886 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2888 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2889 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2890 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2891 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2892 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2894 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2895 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2896 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2897 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2909 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2910 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2911 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2912 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2942 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2943 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2946 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2974 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2975 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2976 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2977 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2978 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2979 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2980 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2981 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2982 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2983 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2984 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2985 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2986 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003105 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003106# if ENABLE_ASH_ALIAS
3107 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3108# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003109};
3110
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003111# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003112
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003113#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003114
Eric Andersen2870d962001-07-02 17:27:21 +00003115
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003116/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003117
Denis Vlasenko131ae172007-02-18 13:00:19 +00003118#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003119
3120#define ALIASINUSE 1
3121#define ALIASDEAD 2
3122
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003123struct alias {
3124 struct alias *next;
3125 char *name;
3126 char *val;
3127 int flag;
3128};
3129
Denis Vlasenko01631112007-12-16 17:20:38 +00003130
3131static struct alias **atab; // [ATABSIZE];
3132#define INIT_G_alias() do { \
3133 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3134} while (0)
3135
Eric Andersen2870d962001-07-02 17:27:21 +00003136
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003137static struct alias **
3138__lookupalias(const char *name) {
3139 unsigned int hashval;
3140 struct alias **app;
3141 const char *p;
3142 unsigned int ch;
3143
3144 p = name;
3145
3146 ch = (unsigned char)*p;
3147 hashval = ch << 4;
3148 while (ch) {
3149 hashval += ch;
3150 ch = (unsigned char)*++p;
3151 }
3152 app = &atab[hashval % ATABSIZE];
3153
3154 for (; *app; app = &(*app)->next) {
3155 if (strcmp(name, (*app)->name) == 0) {
3156 break;
3157 }
3158 }
3159
3160 return app;
3161}
3162
3163static struct alias *
3164lookupalias(const char *name, int check)
3165{
3166 struct alias *ap = *__lookupalias(name);
3167
3168 if (check && ap && (ap->flag & ALIASINUSE))
3169 return NULL;
3170 return ap;
3171}
3172
3173static struct alias *
3174freealias(struct alias *ap)
3175{
3176 struct alias *next;
3177
3178 if (ap->flag & ALIASINUSE) {
3179 ap->flag |= ALIASDEAD;
3180 return ap;
3181 }
3182
3183 next = ap->next;
3184 free(ap->name);
3185 free(ap->val);
3186 free(ap);
3187 return next;
3188}
Eric Andersencb57d552001-06-28 07:25:16 +00003189
Eric Andersenc470f442003-07-28 09:56:35 +00003190static void
3191setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003192{
3193 struct alias *ap, **app;
3194
3195 app = __lookupalias(name);
3196 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003197 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003198 if (ap) {
3199 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003200 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003201 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003202 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003203 ap->flag &= ~ALIASDEAD;
3204 } else {
3205 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003206 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003207 ap->name = ckstrdup(name);
3208 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003209 /*ap->flag = 0; - ckzalloc did it */
3210 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003211 *app = ap;
3212 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003213 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003214}
3215
Eric Andersenc470f442003-07-28 09:56:35 +00003216static int
3217unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003218{
Eric Andersencb57d552001-06-28 07:25:16 +00003219 struct alias **app;
3220
3221 app = __lookupalias(name);
3222
3223 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003224 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003225 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003226 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003227 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003228 }
3229
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003230 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003231}
3232
Eric Andersenc470f442003-07-28 09:56:35 +00003233static void
3234rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003235{
Eric Andersencb57d552001-06-28 07:25:16 +00003236 struct alias *ap, **app;
3237 int i;
3238
Denis Vlasenkob012b102007-02-19 22:43:01 +00003239 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003240 for (i = 0; i < ATABSIZE; i++) {
3241 app = &atab[i];
3242 for (ap = *app; ap; ap = *app) {
3243 *app = freealias(*app);
3244 if (ap == *app) {
3245 app = &ap->next;
3246 }
3247 }
3248 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003249 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003250}
3251
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003252static void
3253printalias(const struct alias *ap)
3254{
3255 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3256}
3257
Eric Andersencb57d552001-06-28 07:25:16 +00003258/*
3259 * TODO - sort output
3260 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003261static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003262aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003263{
3264 char *n, *v;
3265 int ret = 0;
3266 struct alias *ap;
3267
Denis Vlasenko68404f12008-03-17 09:00:54 +00003268 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003269 int i;
3270
Denis Vlasenko68404f12008-03-17 09:00:54 +00003271 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003272 for (ap = atab[i]; ap; ap = ap->next) {
3273 printalias(ap);
3274 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003275 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003276 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003277 }
3278 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003279 v = strchr(n+1, '=');
3280 if (v == NULL) { /* n+1: funny ksh stuff */
3281 ap = *__lookupalias(n);
3282 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003283 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003284 ret = 1;
3285 } else
3286 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003287 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003288 *v++ = '\0';
3289 setalias(n, v);
3290 }
3291 }
3292
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003293 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003294}
3295
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003296static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003297unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003298{
3299 int i;
3300
3301 while ((i = nextopt("a")) != '\0') {
3302 if (i == 'a') {
3303 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003304 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003305 }
3306 }
3307 for (i = 0; *argptr; argptr++) {
3308 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003309 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003310 i = 1;
3311 }
3312 }
3313
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003314 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003315}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003316
Denis Vlasenko131ae172007-02-18 13:00:19 +00003317#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003318
Eric Andersenc470f442003-07-28 09:56:35 +00003319
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320/* ============ jobs.c */
3321
3322/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003323#define FORK_FG 0
3324#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003325#define FORK_NOJOB 2
3326
3327/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003328#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3329#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3330#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332/*
3333 * A job structure contains information about a job. A job is either a
3334 * single process or a set of processes contained in a pipeline. In the
3335 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3336 * array of pids.
3337 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003339 pid_t ps_pid; /* process id */
3340 int ps_status; /* last process status from wait() */
3341 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342};
3343
3344struct job {
3345 struct procstat ps0; /* status of process */
3346 struct procstat *ps; /* status or processes when more than one */
3347#if JOBS
3348 int stopstatus; /* status of a stopped job */
3349#endif
3350 uint32_t
3351 nprocs: 16, /* number of processes */
3352 state: 8,
3353#define JOBRUNNING 0 /* at least one proc running */
3354#define JOBSTOPPED 1 /* all procs are stopped */
3355#define JOBDONE 2 /* all procs are completed */
3356#if JOBS
3357 sigint: 1, /* job was killed by SIGINT */
3358 jobctl: 1, /* job running under job control */
3359#endif
3360 waited: 1, /* true if this entry has been waited for */
3361 used: 1, /* true if this entry is in used */
3362 changed: 1; /* true if status has changed */
3363 struct job *prev_job; /* previous job */
3364};
3365
Denis Vlasenko68404f12008-03-17 09:00:54 +00003366static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003367static int forkshell(struct job *, union node *, int);
3368static int waitforjob(struct job *);
3369
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003370#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003371enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003372#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003374static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003375static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003376#endif
3377
3378/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003379 * Ignore a signal.
3380 */
3381static void
3382ignoresig(int signo)
3383{
3384 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3385 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3386 /* No, need to do it */
3387 signal(signo, SIG_IGN);
3388 }
3389 sigmode[signo - 1] = S_HARD_IGN;
3390}
3391
3392/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003393 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003394 */
3395static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003396signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003397{
3398 gotsig[signo - 1] = 1;
3399
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003400 if (signo == SIGINT && !trap[SIGINT]) {
3401 if (!suppress_int) {
3402 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003403 raise_interrupt(); /* does not return */
3404 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003405 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003406 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003407 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003408 }
3409}
3410
3411/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003412 * Set the signal handler for the specified signal. The routine figures
3413 * out what it should be set to.
3414 */
3415static void
3416setsignal(int signo)
3417{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003418 char *t;
3419 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003420 struct sigaction act;
3421
3422 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003423 new_act = S_DFL;
3424 if (t != NULL) { /* trap for this sig is set */
3425 new_act = S_CATCH;
3426 if (t[0] == '\0') /* trap is "": ignore this sig */
3427 new_act = S_IGN;
3428 }
3429
3430 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003431 switch (signo) {
3432 case SIGINT:
3433 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003434 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003435 break;
3436 case SIGQUIT:
3437#if DEBUG
3438 if (debug)
3439 break;
3440#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003441 /* man bash:
3442 * "In all cases, bash ignores SIGQUIT. Non-builtin
3443 * commands run by bash have signal handlers
3444 * set to the values inherited by the shell
3445 * from its parent". */
3446 new_act = S_IGN;
3447 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003448 case SIGTERM:
3449 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003450 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003451 break;
3452#if JOBS
3453 case SIGTSTP:
3454 case SIGTTOU:
3455 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003456 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003457 break;
3458#endif
3459 }
3460 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003461//TODO: if !rootshell, we reset SIGQUIT to DFL,
3462//whereas we have to restore it to what shell got on entry
3463//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003464
3465 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003466 cur_act = *t;
3467 if (cur_act == 0) {
3468 /* current setting is not yet known */
3469 if (sigaction(signo, NULL, &act)) {
3470 /* pretend it worked; maybe we should give a warning,
3471 * but other shells don't. We don't alter sigmode,
3472 * so we retry every time.
3473 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003474 return;
3475 }
3476 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003477 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003478 if (mflag
3479 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3480 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003481 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003482 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003483 }
3484 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003485 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003486 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003487
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003488 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003489 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003490 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003491 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003492 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3493 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003494 break;
3495 case S_IGN:
3496 act.sa_handler = SIG_IGN;
3497 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003498 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003499 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003500
3501 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003502}
3503
3504/* mode flags for set_curjob */
3505#define CUR_DELETE 2
3506#define CUR_RUNNING 1
3507#define CUR_STOPPED 0
3508
3509/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003510#define DOWAIT_NONBLOCK WNOHANG
3511#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003512
3513#if JOBS
3514/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003515static int initialpgrp; //references:2
3516static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003517#endif
3518/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003519static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003520/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003521static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003522/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003523static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003524/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003525static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003526
3527static void
3528set_curjob(struct job *jp, unsigned mode)
3529{
3530 struct job *jp1;
3531 struct job **jpp, **curp;
3532
3533 /* first remove from list */
3534 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003535 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003536 jp1 = *jpp;
3537 if (jp1 == jp)
3538 break;
3539 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003540 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003541 *jpp = jp1->prev_job;
3542
3543 /* Then re-insert in correct position */
3544 jpp = curp;
3545 switch (mode) {
3546 default:
3547#if DEBUG
3548 abort();
3549#endif
3550 case CUR_DELETE:
3551 /* job being deleted */
3552 break;
3553 case CUR_RUNNING:
3554 /* newly created job or backgrounded job,
3555 put after all stopped jobs. */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003556 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003557 jp1 = *jpp;
3558#if JOBS
3559 if (!jp1 || jp1->state != JOBSTOPPED)
3560#endif
3561 break;
3562 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003563 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003564 /* FALLTHROUGH */
3565#if JOBS
3566 case CUR_STOPPED:
3567#endif
3568 /* newly stopped job - becomes curjob */
3569 jp->prev_job = *jpp;
3570 *jpp = jp;
3571 break;
3572 }
3573}
3574
3575#if JOBS || DEBUG
3576static int
3577jobno(const struct job *jp)
3578{
3579 return jp - jobtab + 1;
3580}
3581#endif
3582
3583/*
3584 * Convert a job name to a job structure.
3585 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003586#if !JOBS
3587#define getjob(name, getctl) getjob(name)
3588#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003589static struct job *
3590getjob(const char *name, int getctl)
3591{
3592 struct job *jp;
3593 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003594 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003595 unsigned num;
3596 int c;
3597 const char *p;
3598 char *(*match)(const char *, const char *);
3599
3600 jp = curjob;
3601 p = name;
3602 if (!p)
3603 goto currentjob;
3604
3605 if (*p != '%')
3606 goto err;
3607
3608 c = *++p;
3609 if (!c)
3610 goto currentjob;
3611
3612 if (!p[1]) {
3613 if (c == '+' || c == '%') {
3614 currentjob:
3615 err_msg = "No current job";
3616 goto check;
3617 }
3618 if (c == '-') {
3619 if (jp)
3620 jp = jp->prev_job;
3621 err_msg = "No previous job";
3622 check:
3623 if (!jp)
3624 goto err;
3625 goto gotit;
3626 }
3627 }
3628
3629 if (is_number(p)) {
3630 num = atoi(p);
3631 if (num < njobs) {
3632 jp = jobtab + num - 1;
3633 if (jp->used)
3634 goto gotit;
3635 goto err;
3636 }
3637 }
3638
3639 match = prefix;
3640 if (*p == '?') {
3641 match = strstr;
3642 p++;
3643 }
3644
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003645 found = NULL;
3646 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003647 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003648 if (found)
3649 goto err;
3650 found = jp;
3651 err_msg = "%s: ambiguous";
3652 }
3653 jp = jp->prev_job;
3654 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003655 if (!found)
3656 goto err;
3657 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003658
3659 gotit:
3660#if JOBS
3661 err_msg = "job %s not created under job control";
3662 if (getctl && jp->jobctl == 0)
3663 goto err;
3664#endif
3665 return jp;
3666 err:
3667 ash_msg_and_raise_error(err_msg, name);
3668}
3669
3670/*
3671 * Mark a job structure as unused.
3672 */
3673static void
3674freejob(struct job *jp)
3675{
3676 struct procstat *ps;
3677 int i;
3678
3679 INT_OFF;
3680 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003681 if (ps->ps_cmd != nullstr)
3682 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003683 }
3684 if (jp->ps != &jp->ps0)
3685 free(jp->ps);
3686 jp->used = 0;
3687 set_curjob(jp, CUR_DELETE);
3688 INT_ON;
3689}
3690
3691#if JOBS
3692static void
3693xtcsetpgrp(int fd, pid_t pgrp)
3694{
3695 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003696 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003697}
3698
3699/*
3700 * Turn job control on and off.
3701 *
3702 * Note: This code assumes that the third arg to ioctl is a character
3703 * pointer, which is true on Berkeley systems but not System V. Since
3704 * System V doesn't have job control yet, this isn't a problem now.
3705 *
3706 * Called with interrupts off.
3707 */
3708static void
3709setjobctl(int on)
3710{
3711 int fd;
3712 int pgrp;
3713
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003714 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003715 return;
3716 if (on) {
3717 int ofd;
3718 ofd = fd = open(_PATH_TTY, O_RDWR);
3719 if (fd < 0) {
3720 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3721 * That sometimes helps to acquire controlling tty.
3722 * Obviously, a workaround for bugs when someone
3723 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003724 fd = 2;
3725 while (!isatty(fd))
3726 if (--fd < 0)
3727 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003728 }
3729 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003730 if (ofd >= 0)
3731 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003732 if (fd < 0)
3733 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003734 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003735 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003736 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003737 pgrp = tcgetpgrp(fd);
3738 if (pgrp < 0) {
3739 out:
3740 ash_msg("can't access tty; job control turned off");
3741 mflag = on = 0;
3742 goto close;
3743 }
3744 if (pgrp == getpgrp())
3745 break;
3746 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003747 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003748 initialpgrp = pgrp;
3749
3750 setsignal(SIGTSTP);
3751 setsignal(SIGTTOU);
3752 setsignal(SIGTTIN);
3753 pgrp = rootpid;
3754 setpgid(0, pgrp);
3755 xtcsetpgrp(fd, pgrp);
3756 } else {
3757 /* turning job control off */
3758 fd = ttyfd;
3759 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003760 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003761 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003762 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003763 setpgid(0, pgrp);
3764 setsignal(SIGTSTP);
3765 setsignal(SIGTTOU);
3766 setsignal(SIGTTIN);
3767 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003768 if (fd >= 0)
3769 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770 fd = -1;
3771 }
3772 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003773 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003774}
3775
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003776static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003777killcmd(int argc, char **argv)
3778{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003779 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003780 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003781 do {
3782 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003783 /*
3784 * "kill %N" - job kill
3785 * Converting to pgrp / pid kill
3786 */
3787 struct job *jp;
3788 char *dst;
3789 int j, n;
3790
3791 jp = getjob(argv[i], 0);
3792 /*
3793 * In jobs started under job control, we signal
3794 * entire process group by kill -PGRP_ID.
3795 * This happens, f.e., in interactive shell.
3796 *
3797 * Otherwise, we signal each child via
3798 * kill PID1 PID2 PID3.
3799 * Testcases:
3800 * sh -c 'sleep 1|sleep 1 & kill %1'
3801 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3802 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3803 */
3804 n = jp->nprocs; /* can't be 0 (I hope) */
3805 if (jp->jobctl)
3806 n = 1;
3807 dst = alloca(n * sizeof(int)*4);
3808 argv[i] = dst;
3809 for (j = 0; j < n; j++) {
3810 struct procstat *ps = &jp->ps[j];
3811 /* Skip non-running and not-stopped members
3812 * (i.e. dead members) of the job
3813 */
3814 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3815 continue;
3816 /*
3817 * kill_main has matching code to expect
3818 * leading space. Needed to not confuse
3819 * negative pids with "kill -SIGNAL_NO" syntax
3820 */
3821 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3822 }
3823 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003824 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003825 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003826 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003827 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003828}
3829
3830static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003831showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003832{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003833 struct procstat *ps;
3834 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003835
Denys Vlasenko285ad152009-12-04 23:02:27 +01003836 psend = jp->ps + jp->nprocs;
3837 for (ps = jp->ps + 1; ps < psend; ps++)
3838 printf(" | %s", ps->ps_cmd);
3839 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840 flush_stdout_stderr();
3841}
3842
3843
3844static int
3845restartjob(struct job *jp, int mode)
3846{
3847 struct procstat *ps;
3848 int i;
3849 int status;
3850 pid_t pgid;
3851
3852 INT_OFF;
3853 if (jp->state == JOBDONE)
3854 goto out;
3855 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003856 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003857 if (mode == FORK_FG)
3858 xtcsetpgrp(ttyfd, pgid);
3859 killpg(pgid, SIGCONT);
3860 ps = jp->ps;
3861 i = jp->nprocs;
3862 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003863 if (WIFSTOPPED(ps->ps_status)) {
3864 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003865 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003866 ps++;
3867 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003868 out:
3869 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3870 INT_ON;
3871 return status;
3872}
3873
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003874static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003875fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003876{
3877 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003878 int mode;
3879 int retval;
3880
3881 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3882 nextopt(nullstr);
3883 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003884 do {
3885 jp = getjob(*argv, 1);
3886 if (mode == FORK_BG) {
3887 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003888 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003890 out1str(jp->ps[0].ps_cmd);
3891 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003892 retval = restartjob(jp, mode);
3893 } while (*argv && *++argv);
3894 return retval;
3895}
3896#endif
3897
3898static int
3899sprint_status(char *s, int status, int sigonly)
3900{
3901 int col;
3902 int st;
3903
3904 col = 0;
3905 if (!WIFEXITED(status)) {
3906#if JOBS
3907 if (WIFSTOPPED(status))
3908 st = WSTOPSIG(status);
3909 else
3910#endif
3911 st = WTERMSIG(status);
3912 if (sigonly) {
3913 if (st == SIGINT || st == SIGPIPE)
3914 goto out;
3915#if JOBS
3916 if (WIFSTOPPED(status))
3917 goto out;
3918#endif
3919 }
3920 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003921//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003922 col = fmtstr(s, 32, strsignal(st));
3923 if (WCOREDUMP(status)) {
3924 col += fmtstr(s + col, 16, " (core dumped)");
3925 }
3926 } else if (!sigonly) {
3927 st = WEXITSTATUS(status);
3928 if (st)
3929 col = fmtstr(s, 16, "Done(%d)", st);
3930 else
3931 col = fmtstr(s, 16, "Done");
3932 }
3933 out:
3934 return col;
3935}
3936
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003937static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003938dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003939{
3940 int pid;
3941 int status;
3942 struct job *jp;
3943 struct job *thisjob;
3944 int state;
3945
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003946 TRACE(("dowait(0x%x) called\n", wait_flags));
3947
3948 /* Do a wait system call. If job control is compiled in, we accept
3949 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3950 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003951 if (doing_jobctl)
3952 wait_flags |= WUNTRACED;
3953 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003954 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3955 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003956 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003958
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003959 INT_OFF;
3960 thisjob = NULL;
3961 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003962 struct procstat *ps;
3963 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964 if (jp->state == JOBDONE)
3965 continue;
3966 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003967 ps = jp->ps;
3968 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003969 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003970 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971 TRACE(("Job %d: changing status of proc %d "
3972 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003973 jobno(jp), pid, ps->ps_status, status));
3974 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003975 thisjob = jp;
3976 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003977 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003978 state = JOBRUNNING;
3979#if JOBS
3980 if (state == JOBRUNNING)
3981 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003982 if (WIFSTOPPED(ps->ps_status)) {
3983 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003984 state = JOBSTOPPED;
3985 }
3986#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003987 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003988 if (thisjob)
3989 goto gotjob;
3990 }
3991#if JOBS
3992 if (!WIFSTOPPED(status))
3993#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003994 jobless--;
3995 goto out;
3996
3997 gotjob:
3998 if (state != JOBRUNNING) {
3999 thisjob->changed = 1;
4000
4001 if (thisjob->state != state) {
4002 TRACE(("Job %d: changing state from %d to %d\n",
4003 jobno(thisjob), thisjob->state, state));
4004 thisjob->state = state;
4005#if JOBS
4006 if (state == JOBSTOPPED) {
4007 set_curjob(thisjob, CUR_STOPPED);
4008 }
4009#endif
4010 }
4011 }
4012
4013 out:
4014 INT_ON;
4015
4016 if (thisjob && thisjob == job) {
4017 char s[48 + 1];
4018 int len;
4019
4020 len = sprint_status(s, status, 1);
4021 if (len) {
4022 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004023 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004024 out2str(s);
4025 }
4026 }
4027 return pid;
4028}
4029
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004030static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004031blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004032{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004033 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004034 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004035 raise_exception(EXSIG);
4036 return pid;
4037}
4038
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004039#if JOBS
4040static void
4041showjob(FILE *out, struct job *jp, int mode)
4042{
4043 struct procstat *ps;
4044 struct procstat *psend;
4045 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004046 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004047 char s[80];
4048
4049 ps = jp->ps;
4050
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004051 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004053 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054 return;
4055 }
4056
4057 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004058 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059
4060 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004061 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004062 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004063 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004065 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004066 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004067
4068 psend = ps + jp->nprocs;
4069
4070 if (jp->state == JOBRUNNING) {
4071 strcpy(s + col, "Running");
4072 col += sizeof("Running") - 1;
4073 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004074 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004075 if (jp->state == JOBSTOPPED)
4076 status = jp->stopstatus;
4077 col += sprint_status(s + col, status, 0);
4078 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004079 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004080
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004081 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4082 * or prints several "PID | <cmdN>" lines,
4083 * depending on SHOW_PIDS bit.
4084 * We do not print status of individual processes
4085 * between PID and <cmdN>. bash does it, but not very well:
4086 * first line shows overall job status, not process status,
4087 * making it impossible to know 1st process status.
4088 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004089 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004090 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004091 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004092 s[0] = '\0';
4093 col = 33;
4094 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004095 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004097 fprintf(out, "%s%*c%s%s",
4098 s,
4099 33 - col >= 0 ? 33 - col : 0, ' ',
4100 ps == jp->ps ? "" : "| ",
4101 ps->ps_cmd
4102 );
4103 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004104 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004105
4106 jp->changed = 0;
4107
4108 if (jp->state == JOBDONE) {
4109 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4110 freejob(jp);
4111 }
4112}
4113
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004114/*
4115 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4116 * statuses have changed since the last call to showjobs.
4117 */
4118static void
4119showjobs(FILE *out, int mode)
4120{
4121 struct job *jp;
4122
Denys Vlasenko883cea42009-07-11 15:31:59 +02004123 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004124
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004125 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004126 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004127 continue;
4128
4129 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004130 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004131 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004132 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004133 }
4134}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004135
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004136static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004137jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004138{
4139 int mode, m;
4140
4141 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004142 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004143 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004144 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004145 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004146 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004147 }
4148
4149 argv = argptr;
4150 if (*argv) {
4151 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004152 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004153 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004154 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004155 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004156 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004157
4158 return 0;
4159}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004160#endif /* JOBS */
4161
Michael Abbott359da5e2009-12-04 23:03:29 +01004162/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004163static int
4164getstatus(struct job *job)
4165{
4166 int status;
4167 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004168 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004169
Michael Abbott359da5e2009-12-04 23:03:29 +01004170 /* Fetch last member's status */
4171 ps = job->ps + job->nprocs - 1;
4172 status = ps->ps_status;
4173 if (pipefail) {
4174 /* "set -o pipefail" mode: use last _nonzero_ status */
4175 while (status == 0 && --ps >= job->ps)
4176 status = ps->ps_status;
4177 }
4178
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179 retval = WEXITSTATUS(status);
4180 if (!WIFEXITED(status)) {
4181#if JOBS
4182 retval = WSTOPSIG(status);
4183 if (!WIFSTOPPED(status))
4184#endif
4185 {
4186 /* XXX: limits number of signals */
4187 retval = WTERMSIG(status);
4188#if JOBS
4189 if (retval == SIGINT)
4190 job->sigint = 1;
4191#endif
4192 }
4193 retval += 128;
4194 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004195 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004196 jobno(job), job->nprocs, status, retval));
4197 return retval;
4198}
4199
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004200static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004201waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004202{
4203 struct job *job;
4204 int retval;
4205 struct job *jp;
4206
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004207 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004208 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004209
4210 nextopt(nullstr);
4211 retval = 0;
4212
4213 argv = argptr;
4214 if (!*argv) {
4215 /* wait for all jobs */
4216 for (;;) {
4217 jp = curjob;
4218 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004219 if (!jp) /* no running procs */
4220 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004221 if (jp->state == JOBRUNNING)
4222 break;
4223 jp->waited = 1;
4224 jp = jp->prev_job;
4225 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004226 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004227 /* man bash:
4228 * "When bash is waiting for an asynchronous command via
4229 * the wait builtin, the reception of a signal for which a trap
4230 * has been set will cause the wait builtin to return immediately
4231 * with an exit status greater than 128, immediately after which
4232 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004233 *
4234 * blocking_wait_with_raise_on_sig raises signal handlers
4235 * if it gets no pid (pid < 0). However,
4236 * if child sends us a signal *and immediately exits*,
4237 * blocking_wait_with_raise_on_sig gets pid > 0
4238 * and does not handle pending_sig. Check this case: */
4239 if (pending_sig)
4240 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004241 }
4242 }
4243
4244 retval = 127;
4245 do {
4246 if (**argv != '%') {
4247 pid_t pid = number(*argv);
4248 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004249 while (1) {
4250 if (!job)
4251 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004252 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004253 break;
4254 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004255 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004256 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004257 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004258 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004259 /* loop until process terminated or stopped */
4260 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004261 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004262 job->waited = 1;
4263 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004264 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004265 } while (*++argv);
4266
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004267 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004268 return retval;
4269}
4270
4271static struct job *
4272growjobtab(void)
4273{
4274 size_t len;
4275 ptrdiff_t offset;
4276 struct job *jp, *jq;
4277
4278 len = njobs * sizeof(*jp);
4279 jq = jobtab;
4280 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4281
4282 offset = (char *)jp - (char *)jq;
4283 if (offset) {
4284 /* Relocate pointers */
4285 size_t l = len;
4286
4287 jq = (struct job *)((char *)jq + l);
4288 while (l) {
4289 l -= sizeof(*jp);
4290 jq--;
4291#define joff(p) ((struct job *)((char *)(p) + l))
4292#define jmove(p) (p) = (void *)((char *)(p) + offset)
4293 if (joff(jp)->ps == &jq->ps0)
4294 jmove(joff(jp)->ps);
4295 if (joff(jp)->prev_job)
4296 jmove(joff(jp)->prev_job);
4297 }
4298 if (curjob)
4299 jmove(curjob);
4300#undef joff
4301#undef jmove
4302 }
4303
4304 njobs += 4;
4305 jobtab = jp;
4306 jp = (struct job *)((char *)jp + len);
4307 jq = jp + 3;
4308 do {
4309 jq->used = 0;
4310 } while (--jq >= jp);
4311 return jp;
4312}
4313
4314/*
4315 * Return a new job structure.
4316 * Called with interrupts off.
4317 */
4318static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004319makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004320{
4321 int i;
4322 struct job *jp;
4323
4324 for (i = njobs, jp = jobtab; ; jp++) {
4325 if (--i < 0) {
4326 jp = growjobtab();
4327 break;
4328 }
4329 if (jp->used == 0)
4330 break;
4331 if (jp->state != JOBDONE || !jp->waited)
4332 continue;
4333#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004334 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004335 continue;
4336#endif
4337 freejob(jp);
4338 break;
4339 }
4340 memset(jp, 0, sizeof(*jp));
4341#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004342 /* jp->jobctl is a bitfield.
4343 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004344 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004345 jp->jobctl = 1;
4346#endif
4347 jp->prev_job = curjob;
4348 curjob = jp;
4349 jp->used = 1;
4350 jp->ps = &jp->ps0;
4351 if (nprocs > 1) {
4352 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4353 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004354 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004355 jobno(jp)));
4356 return jp;
4357}
4358
4359#if JOBS
4360/*
4361 * Return a string identifying a command (to be printed by the
4362 * jobs command).
4363 */
4364static char *cmdnextc;
4365
4366static void
4367cmdputs(const char *s)
4368{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004369 static const char vstype[VSTYPE + 1][3] = {
4370 "", "}", "-", "+", "?", "=",
4371 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004372 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004373 };
4374
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004375 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004376 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004377 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004378 unsigned char c;
4379 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004380 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004381
Denys Vlasenko46a14772009-12-10 21:27:13 +01004382 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004383 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4384 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004385 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004386 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 switch (c) {
4388 case CTLESC:
4389 c = *p++;
4390 break;
4391 case CTLVAR:
4392 subtype = *p++;
4393 if ((subtype & VSTYPE) == VSLENGTH)
4394 str = "${#";
4395 else
4396 str = "${";
4397 if (!(subtype & VSQUOTE) == !(quoted & 1))
4398 goto dostr;
4399 quoted ^= 1;
4400 c = '"';
4401 break;
4402 case CTLENDVAR:
4403 str = "\"}" + !(quoted & 1);
4404 quoted >>= 1;
4405 subtype = 0;
4406 goto dostr;
4407 case CTLBACKQ:
4408 str = "$(...)";
4409 goto dostr;
4410 case CTLBACKQ+CTLQUOTE:
4411 str = "\"$(...)\"";
4412 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004413#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004414 case CTLARI:
4415 str = "$((";
4416 goto dostr;
4417 case CTLENDARI:
4418 str = "))";
4419 goto dostr;
4420#endif
4421 case CTLQUOTEMARK:
4422 quoted ^= 1;
4423 c = '"';
4424 break;
4425 case '=':
4426 if (subtype == 0)
4427 break;
4428 if ((subtype & VSTYPE) != VSNORMAL)
4429 quoted <<= 1;
4430 str = vstype[subtype & VSTYPE];
4431 if (subtype & VSNUL)
4432 c = ':';
4433 else
4434 goto checkstr;
4435 break;
4436 case '\'':
4437 case '\\':
4438 case '"':
4439 case '$':
4440 /* These can only happen inside quotes */
4441 cc[0] = c;
4442 str = cc;
4443 c = '\\';
4444 break;
4445 default:
4446 break;
4447 }
4448 USTPUTC(c, nextc);
4449 checkstr:
4450 if (!str)
4451 continue;
4452 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004453 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004454 USTPUTC(c, nextc);
4455 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004456 } /* while *p++ not NUL */
4457
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004458 if (quoted & 1) {
4459 USTPUTC('"', nextc);
4460 }
4461 *nextc = 0;
4462 cmdnextc = nextc;
4463}
4464
4465/* cmdtxt() and cmdlist() call each other */
4466static void cmdtxt(union node *n);
4467
4468static void
4469cmdlist(union node *np, int sep)
4470{
4471 for (; np; np = np->narg.next) {
4472 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004473 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004474 cmdtxt(np);
4475 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004476 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004477 }
4478}
4479
4480static void
4481cmdtxt(union node *n)
4482{
4483 union node *np;
4484 struct nodelist *lp;
4485 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004486
4487 if (!n)
4488 return;
4489 switch (n->type) {
4490 default:
4491#if DEBUG
4492 abort();
4493#endif
4494 case NPIPE:
4495 lp = n->npipe.cmdlist;
4496 for (;;) {
4497 cmdtxt(lp->n);
4498 lp = lp->next;
4499 if (!lp)
4500 break;
4501 cmdputs(" | ");
4502 }
4503 break;
4504 case NSEMI:
4505 p = "; ";
4506 goto binop;
4507 case NAND:
4508 p = " && ";
4509 goto binop;
4510 case NOR:
4511 p = " || ";
4512 binop:
4513 cmdtxt(n->nbinary.ch1);
4514 cmdputs(p);
4515 n = n->nbinary.ch2;
4516 goto donode;
4517 case NREDIR:
4518 case NBACKGND:
4519 n = n->nredir.n;
4520 goto donode;
4521 case NNOT:
4522 cmdputs("!");
4523 n = n->nnot.com;
4524 donode:
4525 cmdtxt(n);
4526 break;
4527 case NIF:
4528 cmdputs("if ");
4529 cmdtxt(n->nif.test);
4530 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004531 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004532 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004533 cmdputs("; else ");
4534 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004535 } else {
4536 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537 }
4538 p = "; fi";
4539 goto dotail;
4540 case NSUBSHELL:
4541 cmdputs("(");
4542 n = n->nredir.n;
4543 p = ")";
4544 goto dotail;
4545 case NWHILE:
4546 p = "while ";
4547 goto until;
4548 case NUNTIL:
4549 p = "until ";
4550 until:
4551 cmdputs(p);
4552 cmdtxt(n->nbinary.ch1);
4553 n = n->nbinary.ch2;
4554 p = "; done";
4555 dodo:
4556 cmdputs("; do ");
4557 dotail:
4558 cmdtxt(n);
4559 goto dotail2;
4560 case NFOR:
4561 cmdputs("for ");
4562 cmdputs(n->nfor.var);
4563 cmdputs(" in ");
4564 cmdlist(n->nfor.args, 1);
4565 n = n->nfor.body;
4566 p = "; done";
4567 goto dodo;
4568 case NDEFUN:
4569 cmdputs(n->narg.text);
4570 p = "() { ... }";
4571 goto dotail2;
4572 case NCMD:
4573 cmdlist(n->ncmd.args, 1);
4574 cmdlist(n->ncmd.redirect, 0);
4575 break;
4576 case NARG:
4577 p = n->narg.text;
4578 dotail2:
4579 cmdputs(p);
4580 break;
4581 case NHERE:
4582 case NXHERE:
4583 p = "<<...";
4584 goto dotail2;
4585 case NCASE:
4586 cmdputs("case ");
4587 cmdputs(n->ncase.expr->narg.text);
4588 cmdputs(" in ");
4589 for (np = n->ncase.cases; np; np = np->nclist.next) {
4590 cmdtxt(np->nclist.pattern);
4591 cmdputs(") ");
4592 cmdtxt(np->nclist.body);
4593 cmdputs(";; ");
4594 }
4595 p = "esac";
4596 goto dotail2;
4597 case NTO:
4598 p = ">";
4599 goto redir;
4600 case NCLOBBER:
4601 p = ">|";
4602 goto redir;
4603 case NAPPEND:
4604 p = ">>";
4605 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004606#if ENABLE_ASH_BASH_COMPAT
4607 case NTO2:
4608#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004609 case NTOFD:
4610 p = ">&";
4611 goto redir;
4612 case NFROM:
4613 p = "<";
4614 goto redir;
4615 case NFROMFD:
4616 p = "<&";
4617 goto redir;
4618 case NFROMTO:
4619 p = "<>";
4620 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004621 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004622 cmdputs(p);
4623 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004624 cmdputs(utoa(n->ndup.dupfd));
4625 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004626 }
4627 n = n->nfile.fname;
4628 goto donode;
4629 }
4630}
4631
4632static char *
4633commandtext(union node *n)
4634{
4635 char *name;
4636
4637 STARTSTACKSTR(cmdnextc);
4638 cmdtxt(n);
4639 name = stackblock();
4640 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4641 name, cmdnextc, cmdnextc));
4642 return ckstrdup(name);
4643}
4644#endif /* JOBS */
4645
4646/*
4647 * Fork off a subshell. If we are doing job control, give the subshell its
4648 * own process group. Jp is a job structure that the job is to be added to.
4649 * N is the command that will be evaluated by the child. Both jp and n may
4650 * be NULL. The mode parameter can be one of the following:
4651 * FORK_FG - Fork off a foreground process.
4652 * FORK_BG - Fork off a background process.
4653 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4654 * process group even if job control is on.
4655 *
4656 * When job control is turned off, background processes have their standard
4657 * input redirected to /dev/null (except for the second and later processes
4658 * in a pipeline).
4659 *
4660 * Called with interrupts off.
4661 */
4662/*
4663 * Clear traps on a fork.
4664 */
4665static void
4666clear_traps(void)
4667{
4668 char **tp;
4669
4670 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004671 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004672 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004673 if (trap_ptr == trap)
4674 free(*tp);
4675 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004676 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004677 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004678 setsignal(tp - trap);
4679 INT_ON;
4680 }
4681 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004682 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004683}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004684
4685/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004686static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004687
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004688/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004689static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004690forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004691{
4692 int oldlvl;
4693
4694 TRACE(("Child shell %d\n", getpid()));
4695 oldlvl = shlvl;
4696 shlvl++;
4697
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004698 /* man bash: "Non-builtin commands run by bash have signal handlers
4699 * set to the values inherited by the shell from its parent".
4700 * Do we do it correctly? */
4701
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004702 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004703
4704 if (mode == FORK_NOJOB /* is it `xxx` ? */
4705 && n && n->type == NCMD /* is it single cmd? */
4706 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004707 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004708 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4709 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4710 ) {
4711 TRACE(("Trap hack\n"));
4712 /* Awful hack for `trap` or $(trap).
4713 *
4714 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4715 * contains an example where "trap" is executed in a subshell:
4716 *
4717 * save_traps=$(trap)
4718 * ...
4719 * eval "$save_traps"
4720 *
4721 * Standard does not say that "trap" in subshell shall print
4722 * parent shell's traps. It only says that its output
4723 * must have suitable form, but then, in the above example
4724 * (which is not supposed to be normative), it implies that.
4725 *
4726 * bash (and probably other shell) does implement it
4727 * (traps are reset to defaults, but "trap" still shows them),
4728 * but as a result, "trap" logic is hopelessly messed up:
4729 *
4730 * # trap
4731 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4732 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4733 * # true | trap <--- trap is in subshell - no output (ditto)
4734 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4735 * trap -- 'echo Ho' SIGWINCH
4736 * # echo `(trap)` <--- in subshell in subshell - output
4737 * trap -- 'echo Ho' SIGWINCH
4738 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4739 * trap -- 'echo Ho' SIGWINCH
4740 *
4741 * The rules when to forget and when to not forget traps
4742 * get really complex and nonsensical.
4743 *
4744 * Our solution: ONLY bare $(trap) or `trap` is special.
4745 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004746 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004747 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004748 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004749 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004750 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004751#if JOBS
4752 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004753 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004754 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004755 pid_t pgrp;
4756
4757 if (jp->nprocs == 0)
4758 pgrp = getpid();
4759 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004760 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004761 /* this can fail because we are doing it in the parent also */
4762 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004763 if (mode == FORK_FG)
4764 xtcsetpgrp(ttyfd, pgrp);
4765 setsignal(SIGTSTP);
4766 setsignal(SIGTTOU);
4767 } else
4768#endif
4769 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004770 /* man bash: "When job control is not in effect,
4771 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004772 ignoresig(SIGINT);
4773 ignoresig(SIGQUIT);
4774 if (jp->nprocs == 0) {
4775 close(0);
4776 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004777 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004778 }
4779 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004780 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004781 if (iflag) { /* why if iflag only? */
4782 setsignal(SIGINT);
4783 setsignal(SIGTERM);
4784 }
4785 /* man bash:
4786 * "In all cases, bash ignores SIGQUIT. Non-builtin
4787 * commands run by bash have signal handlers
4788 * set to the values inherited by the shell
4789 * from its parent".
4790 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004791 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004792 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004793#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004794 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004795 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004796 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004797 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004798 /* "jobs": we do not want to clear job list for it,
4799 * instead we remove only _its_ own_ job from job list.
4800 * This makes "jobs .... | cat" more useful.
4801 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004802 freejob(curjob);
4803 return;
4804 }
4805#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004806 for (jp = curjob; jp; jp = jp->prev_job)
4807 freejob(jp);
4808 jobless = 0;
4809}
4810
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004811/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004812#if !JOBS
4813#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4814#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004815static void
4816forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4817{
4818 TRACE(("In parent shell: child = %d\n", pid));
4819 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004820 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4821 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004822 jobless++;
4823 return;
4824 }
4825#if JOBS
4826 if (mode != FORK_NOJOB && jp->jobctl) {
4827 int pgrp;
4828
4829 if (jp->nprocs == 0)
4830 pgrp = pid;
4831 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004832 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004833 /* This can fail because we are doing it in the child also */
4834 setpgid(pid, pgrp);
4835 }
4836#endif
4837 if (mode == FORK_BG) {
4838 backgndpid = pid; /* set $! */
4839 set_curjob(jp, CUR_RUNNING);
4840 }
4841 if (jp) {
4842 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004843 ps->ps_pid = pid;
4844 ps->ps_status = -1;
4845 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004846#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004847 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004848 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004849#endif
4850 }
4851}
4852
4853static int
4854forkshell(struct job *jp, union node *n, int mode)
4855{
4856 int pid;
4857
4858 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4859 pid = fork();
4860 if (pid < 0) {
4861 TRACE(("Fork failed, errno=%d", errno));
4862 if (jp)
4863 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004864 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004865 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004866 if (pid == 0) {
4867 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004868 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004869 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004870 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004871 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004872 return pid;
4873}
4874
4875/*
4876 * Wait for job to finish.
4877 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004878 * Under job control we have the problem that while a child process
4879 * is running interrupts generated by the user are sent to the child
4880 * but not to the shell. This means that an infinite loop started by
4881 * an interactive user may be hard to kill. With job control turned off,
4882 * an interactive user may place an interactive program inside a loop.
4883 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004884 * these interrupts to also abort the loop. The approach we take here
4885 * is to have the shell ignore interrupt signals while waiting for a
4886 * foreground process to terminate, and then send itself an interrupt
4887 * signal if the child process was terminated by an interrupt signal.
4888 * Unfortunately, some programs want to do a bit of cleanup and then
4889 * exit on interrupt; unless these processes terminate themselves by
4890 * sending a signal to themselves (instead of calling exit) they will
4891 * confuse this approach.
4892 *
4893 * Called with interrupts off.
4894 */
4895static int
4896waitforjob(struct job *jp)
4897{
4898 int st;
4899
4900 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004901
4902 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004903 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004904 /* In non-interactive shells, we _can_ get
4905 * a keyboard signal here and be EINTRed,
4906 * but we just loop back, waiting for command to complete.
4907 *
4908 * man bash:
4909 * "If bash is waiting for a command to complete and receives
4910 * a signal for which a trap has been set, the trap
4911 * will not be executed until the command completes."
4912 *
4913 * Reality is that even if trap is not set, bash
4914 * will not act on the signal until command completes.
4915 * Try this. sleep5intoff.c:
4916 * #include <signal.h>
4917 * #include <unistd.h>
4918 * int main() {
4919 * sigset_t set;
4920 * sigemptyset(&set);
4921 * sigaddset(&set, SIGINT);
4922 * sigaddset(&set, SIGQUIT);
4923 * sigprocmask(SIG_BLOCK, &set, NULL);
4924 * sleep(5);
4925 * return 0;
4926 * }
4927 * $ bash -c './sleep5intoff; echo hi'
4928 * ^C^C^C^C <--- pressing ^C once a second
4929 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004930 * $ bash -c './sleep5intoff; echo hi'
4931 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4932 * $ _
4933 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004934 dowait(DOWAIT_BLOCK, jp);
4935 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004936 INT_ON;
4937
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004938 st = getstatus(jp);
4939#if JOBS
4940 if (jp->jobctl) {
4941 xtcsetpgrp(ttyfd, rootpid);
4942 /*
4943 * This is truly gross.
4944 * If we're doing job control, then we did a TIOCSPGRP which
4945 * caused us (the shell) to no longer be in the controlling
4946 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4947 * intuit from the subprocess exit status whether a SIGINT
4948 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4949 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004950 if (jp->sigint) /* TODO: do the same with all signals */
4951 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004952 }
4953 if (jp->state == JOBDONE)
4954#endif
4955 freejob(jp);
4956 return st;
4957}
4958
4959/*
4960 * return 1 if there are stopped jobs, otherwise 0
4961 */
4962static int
4963stoppedjobs(void)
4964{
4965 struct job *jp;
4966 int retval;
4967
4968 retval = 0;
4969 if (job_warning)
4970 goto out;
4971 jp = curjob;
4972 if (jp && jp->state == JOBSTOPPED) {
4973 out2str("You have stopped jobs.\n");
4974 job_warning = 2;
4975 retval++;
4976 }
4977 out:
4978 return retval;
4979}
4980
4981
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004982/* ============ redir.c
4983 *
4984 * Code for dealing with input/output redirection.
4985 */
4986
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004987#undef EMPTY
4988#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004989#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004990#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004991
4992/*
4993 * Open a file in noclobber mode.
4994 * The code was copied from bash.
4995 */
4996static int
4997noclobberopen(const char *fname)
4998{
4999 int r, fd;
5000 struct stat finfo, finfo2;
5001
5002 /*
5003 * If the file exists and is a regular file, return an error
5004 * immediately.
5005 */
5006 r = stat(fname, &finfo);
5007 if (r == 0 && S_ISREG(finfo.st_mode)) {
5008 errno = EEXIST;
5009 return -1;
5010 }
5011
5012 /*
5013 * If the file was not present (r != 0), make sure we open it
5014 * exclusively so that if it is created before we open it, our open
5015 * will fail. Make sure that we do not truncate an existing file.
5016 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5017 * file was not a regular file, we leave O_EXCL off.
5018 */
5019 if (r != 0)
5020 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5021 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5022
5023 /* If the open failed, return the file descriptor right away. */
5024 if (fd < 0)
5025 return fd;
5026
5027 /*
5028 * OK, the open succeeded, but the file may have been changed from a
5029 * non-regular file to a regular file between the stat and the open.
5030 * We are assuming that the O_EXCL open handles the case where FILENAME
5031 * did not exist and is symlinked to an existing file between the stat
5032 * and open.
5033 */
5034
5035 /*
5036 * If we can open it and fstat the file descriptor, and neither check
5037 * revealed that it was a regular file, and the file has not been
5038 * replaced, return the file descriptor.
5039 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005040 if (fstat(fd, &finfo2) == 0
5041 && !S_ISREG(finfo2.st_mode)
5042 && finfo.st_dev == finfo2.st_dev
5043 && finfo.st_ino == finfo2.st_ino
5044 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005046 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005047
5048 /* The file has been replaced. badness. */
5049 close(fd);
5050 errno = EEXIST;
5051 return -1;
5052}
5053
5054/*
5055 * Handle here documents. Normally we fork off a process to write the
5056 * data to a pipe. If the document is short, we can stuff the data in
5057 * the pipe without forking.
5058 */
5059/* openhere needs this forward reference */
5060static void expandhere(union node *arg, int fd);
5061static int
5062openhere(union node *redir)
5063{
5064 int pip[2];
5065 size_t len = 0;
5066
5067 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005068 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005069 if (redir->type == NHERE) {
5070 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005071 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005072 full_write(pip[1], redir->nhere.doc->narg.text, len);
5073 goto out;
5074 }
5075 }
5076 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005077 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005078 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005079 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5080 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5081 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5082 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005083 signal(SIGPIPE, SIG_DFL);
5084 if (redir->type == NHERE)
5085 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005086 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005087 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005088 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005089 }
5090 out:
5091 close(pip[1]);
5092 return pip[0];
5093}
5094
5095static int
5096openredirect(union node *redir)
5097{
5098 char *fname;
5099 int f;
5100
5101 switch (redir->nfile.type) {
5102 case NFROM:
5103 fname = redir->nfile.expfname;
5104 f = open(fname, O_RDONLY);
5105 if (f < 0)
5106 goto eopen;
5107 break;
5108 case NFROMTO:
5109 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005110 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005111 if (f < 0)
5112 goto ecreate;
5113 break;
5114 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005115#if ENABLE_ASH_BASH_COMPAT
5116 case NTO2:
5117#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005118 /* Take care of noclobber mode. */
5119 if (Cflag) {
5120 fname = redir->nfile.expfname;
5121 f = noclobberopen(fname);
5122 if (f < 0)
5123 goto ecreate;
5124 break;
5125 }
5126 /* FALLTHROUGH */
5127 case NCLOBBER:
5128 fname = redir->nfile.expfname;
5129 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5130 if (f < 0)
5131 goto ecreate;
5132 break;
5133 case NAPPEND:
5134 fname = redir->nfile.expfname;
5135 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5136 if (f < 0)
5137 goto ecreate;
5138 break;
5139 default:
5140#if DEBUG
5141 abort();
5142#endif
5143 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005144/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005145// case NTOFD:
5146// case NFROMFD:
5147// f = -1;
5148// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005149 case NHERE:
5150 case NXHERE:
5151 f = openhere(redir);
5152 break;
5153 }
5154
5155 return f;
5156 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005157 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005158 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005159 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160}
5161
5162/*
5163 * Copy a file descriptor to be >= to. Returns -1
5164 * if the source file descriptor is closed, EMPTY if there are no unused
5165 * file descriptors left.
5166 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005167/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5168 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005169enum {
5170 COPYFD_EXACT = (int)~(INT_MAX),
5171 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5172};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005173static int
5174copyfd(int from, int to)
5175{
5176 int newfd;
5177
Denis Vlasenko5a867312008-07-24 19:46:38 +00005178 if (to & COPYFD_EXACT) {
5179 to &= ~COPYFD_EXACT;
5180 /*if (from != to)*/
5181 newfd = dup2(from, to);
5182 } else {
5183 newfd = fcntl(from, F_DUPFD, to);
5184 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005185 if (newfd < 0) {
5186 if (errno == EMFILE)
5187 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005188 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005189 ash_msg_and_raise_error("%d: %m", from);
5190 }
5191 return newfd;
5192}
5193
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005194/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005195struct two_fd_t {
5196 int orig, copy;
5197};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005198struct redirtab {
5199 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005200 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005201 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005202 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005203};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005204#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005205
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005206static int need_to_remember(struct redirtab *rp, int fd)
5207{
5208 int i;
5209
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005210 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005211 return 0;
5212
5213 for (i = 0; i < rp->pair_count; i++) {
5214 if (rp->two_fd[i].orig == fd) {
5215 /* already remembered */
5216 return 0;
5217 }
5218 }
5219 return 1;
5220}
5221
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005222/* "hidden" fd is a fd used to read scripts, or a copy of such */
5223static int is_hidden_fd(struct redirtab *rp, int fd)
5224{
5225 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005226 struct parsefile *pf;
5227
5228 if (fd == -1)
5229 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005230 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005231 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005232 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005233 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005234 * $ ash # running ash interactively
5235 * $ . ./script.sh
5236 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005237 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005238 * it's still ok to use it: "read" builtin uses it,
5239 * why should we cripple "exec" builtin?
5240 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005241 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005242 return 1;
5243 }
5244 pf = pf->prev;
5245 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005246
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005247 if (!rp)
5248 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005249 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005250 fd |= COPYFD_RESTORE;
5251 for (i = 0; i < rp->pair_count; i++) {
5252 if (rp->two_fd[i].copy == fd) {
5253 return 1;
5254 }
5255 }
5256 return 0;
5257}
5258
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005259/*
5260 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5261 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005262 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005263 */
5264/* flags passed to redirect */
5265#define REDIR_PUSH 01 /* save previous values of file descriptors */
5266#define REDIR_SAVEFD2 03 /* set preverrout */
5267static void
5268redirect(union node *redir, int flags)
5269{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005270 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005271 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005272 int i;
5273 int fd;
5274 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005275 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005276
Denis Vlasenko01631112007-12-16 17:20:38 +00005277 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005278 if (!redir) {
5279 return;
5280 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005281
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005282 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005283 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005284 INT_OFF;
5285 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005286 union node *tmp = redir;
5287 do {
5288 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005289#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005290 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005291 sv_pos++;
5292#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005293 tmp = tmp->nfile.next;
5294 } while (tmp);
5295 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005296 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005297 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005298 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005299 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005300 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005301 while (sv_pos > 0) {
5302 sv_pos--;
5303 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5304 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005305 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005306
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005307 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005308 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005309 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005310 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005311 right_fd = redir->ndup.dupfd;
5312 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005313 /* redirect from/to same file descriptor? */
5314 if (right_fd == fd)
5315 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005316 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005317 if (is_hidden_fd(sv, right_fd)) {
5318 errno = EBADF; /* as if it is closed */
5319 ash_msg_and_raise_error("%d: %m", right_fd);
5320 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005321 newfd = -1;
5322 } else {
5323 newfd = openredirect(redir); /* always >= 0 */
5324 if (fd == newfd) {
5325 /* Descriptor wasn't open before redirect.
5326 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005327 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005328 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005329 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005330 continue;
5331 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005332 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005333#if ENABLE_ASH_BASH_COMPAT
5334 redirect_more:
5335#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005336 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005337 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005338 /* Careful to not accidentally "save"
5339 * to the same fd as right side fd in N>&M */
5340 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5341 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005342/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5343 * are closed in popredir() in the child, preventing them from leaking
5344 * into child. (popredir() also cleans up the mess in case of failures)
5345 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005346 if (i == -1) {
5347 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005348 if (i != EBADF) {
5349 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005350 if (newfd >= 0)
5351 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005352 errno = i;
5353 ash_msg_and_raise_error("%d: %m", fd);
5354 /* NOTREACHED */
5355 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005356 /* EBADF: it is not open - good, remember to close it */
5357 remember_to_close:
5358 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005359 } else { /* fd is open, save its copy */
5360 /* "exec fd>&-" should not close fds
5361 * which point to script file(s).
5362 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005363 if (is_hidden_fd(sv, fd))
5364 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005365 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005366 if (fd == 2)
5367 copied_fd2 = i;
5368 sv->two_fd[sv_pos].orig = fd;
5369 sv->two_fd[sv_pos].copy = i;
5370 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005371 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005372 if (newfd < 0) {
5373 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005374 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005375 /* Don't want to trigger debugging */
5376 if (fd != -1)
5377 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005378 } else {
5379 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005380 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005381 } else if (fd != newfd) { /* move newfd to fd */
5382 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005383#if ENABLE_ASH_BASH_COMPAT
5384 if (!(redir->nfile.type == NTO2 && fd == 2))
5385#endif
5386 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005387 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005388#if ENABLE_ASH_BASH_COMPAT
5389 if (redir->nfile.type == NTO2 && fd == 1) {
5390 /* We already redirected it to fd 1, now copy it to 2 */
5391 newfd = 1;
5392 fd = 2;
5393 goto redirect_more;
5394 }
5395#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005396 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005397
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005398 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005399 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5400 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005401}
5402
5403/*
5404 * Undo the effects of the last redirection.
5405 */
5406static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005407popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005408{
5409 struct redirtab *rp;
5410 int i;
5411
Denis Vlasenko01631112007-12-16 17:20:38 +00005412 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005413 return;
5414 INT_OFF;
5415 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005416 for (i = 0; i < rp->pair_count; i++) {
5417 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005418 int copy = rp->two_fd[i].copy;
5419 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005420 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005421 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005422 continue;
5423 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005424 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005425 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005426 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005427 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005428 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005429 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005430 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005431 }
5432 }
5433 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005434 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005435 free(rp);
5436 INT_ON;
5437}
5438
5439/*
5440 * Undo all redirections. Called on error or interrupt.
5441 */
5442
5443/*
5444 * Discard all saved file descriptors.
5445 */
5446static void
5447clearredir(int drop)
5448{
5449 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005450 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005451 if (!redirlist)
5452 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005453 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005454 }
5455}
5456
5457static int
5458redirectsafe(union node *redir, int flags)
5459{
5460 int err;
5461 volatile int saveint;
5462 struct jmploc *volatile savehandler = exception_handler;
5463 struct jmploc jmploc;
5464
5465 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005466 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5467 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005468 if (!err) {
5469 exception_handler = &jmploc;
5470 redirect(redir, flags);
5471 }
5472 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005473 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005474 longjmp(exception_handler->loc, 1);
5475 RESTORE_INT(saveint);
5476 return err;
5477}
5478
5479
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005480/* ============ Routines to expand arguments to commands
5481 *
5482 * We have to deal with backquotes, shell variables, and file metacharacters.
5483 */
5484
Mike Frysinger98c52642009-04-02 10:02:37 +00005485#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005486static arith_t
5487ash_arith(const char *s)
5488{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005489 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005490 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005491
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005492 math_state.lookupvar = lookupvar;
5493 math_state.setvar = setvar2;
5494 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005495
5496 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005497 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005498 if (math_state.errmsg)
5499 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005500 INT_ON;
5501
5502 return result;
5503}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005504#endif
5505
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005506/*
5507 * expandarg flags
5508 */
5509#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5510#define EXP_TILDE 0x2 /* do normal tilde expansion */
5511#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5512#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5513#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5514#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5515#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5516#define EXP_WORD 0x80 /* expand word in parameter expansion */
5517#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5518/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005519 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005520 */
5521#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5522#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5523#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5524#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5525#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5526
5527/*
5528 * Structure specifying which parts of the string should be searched
5529 * for IFS characters.
5530 */
5531struct ifsregion {
5532 struct ifsregion *next; /* next region in list */
5533 int begoff; /* offset of start of region */
5534 int endoff; /* offset of end of region */
5535 int nulonly; /* search for nul bytes only */
5536};
5537
5538struct arglist {
5539 struct strlist *list;
5540 struct strlist **lastp;
5541};
5542
5543/* output of current string */
5544static char *expdest;
5545/* list of back quote expressions */
5546static struct nodelist *argbackq;
5547/* first struct in list of ifs regions */
5548static struct ifsregion ifsfirst;
5549/* last struct in list */
5550static struct ifsregion *ifslastp;
5551/* holds expanded arg list */
5552static struct arglist exparg;
5553
5554/*
5555 * Our own itoa().
5556 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005557#if !ENABLE_SH_MATH_SUPPORT
5558/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5559typedef long arith_t;
5560# define ARITH_FMT "%ld"
5561#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005562static int
5563cvtnum(arith_t num)
5564{
5565 int len;
5566
5567 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005568 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569 STADJUST(len, expdest);
5570 return len;
5571}
5572
5573static size_t
5574esclen(const char *start, const char *p)
5575{
5576 size_t esc = 0;
5577
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005578 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005579 esc++;
5580 }
5581 return esc;
5582}
5583
5584/*
5585 * Remove any CTLESC characters from a string.
5586 */
5587static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005588rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005589{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005590 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005591
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005592 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005593 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005594 unsigned protect_against_glob;
5595 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596
5597 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005598 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005599 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005600
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005601 q = p;
5602 r = str;
5603 if (flag & RMESCAPE_ALLOC) {
5604 size_t len = p - str;
5605 size_t fulllen = len + strlen(p) + 1;
5606
5607 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005608 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005609 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005610 /* p and str may be invalidated by makestrspace */
5611 str = (char *)stackblock() + strloc;
5612 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005613 } else if (flag & RMESCAPE_HEAP) {
5614 r = ckmalloc(fulllen);
5615 } else {
5616 r = stalloc(fulllen);
5617 }
5618 q = r;
5619 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005620 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005621 }
5622 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005623
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005624 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5625 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005626 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005627 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005628 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005629// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5630// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5631// Note: both inquotes and protect_against_glob only affect whether
5632// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005633 inquotes = ~inquotes;
5634 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005635 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005636 continue;
5637 }
5638 if (*p == '\\') {
5639 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005640 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005641 goto copy;
5642 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005643 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005644 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005645 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005646 *q++ = '\\';
5647 }
5648 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005649 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005650 copy:
5651 *q++ = *p++;
5652 }
5653 *q = '\0';
5654 if (flag & RMESCAPE_GROW) {
5655 expdest = r;
5656 STADJUST(q - r + 1, expdest);
5657 }
5658 return r;
5659}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005660#define pmatch(a, b) !fnmatch((a), (b), 0)
5661
5662/*
5663 * Prepare a pattern for a expmeta (internal glob(3)) call.
5664 *
5665 * Returns an stalloced string.
5666 */
5667static char *
5668preglob(const char *pattern, int quoted, int flag)
5669{
5670 flag |= RMESCAPE_GLOB;
5671 if (quoted) {
5672 flag |= RMESCAPE_QUOTED;
5673 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005674 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005675}
5676
5677/*
5678 * Put a string on the stack.
5679 */
5680static void
5681memtodest(const char *p, size_t len, int syntax, int quotes)
5682{
5683 char *q = expdest;
5684
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005685 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005686
5687 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005688 unsigned char c = *p++;
5689 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005690 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005691 if (quotes) {
5692 int n = SIT(c, syntax);
5693 if (n == CCTL || n == CBACK)
5694 USTPUTC(CTLESC, q);
5695 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005696 USTPUTC(c, q);
5697 }
5698
5699 expdest = q;
5700}
5701
5702static void
5703strtodest(const char *p, int syntax, int quotes)
5704{
5705 memtodest(p, strlen(p), syntax, quotes);
5706}
5707
5708/*
5709 * Record the fact that we have to scan this region of the
5710 * string for IFS characters.
5711 */
5712static void
5713recordregion(int start, int end, int nulonly)
5714{
5715 struct ifsregion *ifsp;
5716
5717 if (ifslastp == NULL) {
5718 ifsp = &ifsfirst;
5719 } else {
5720 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005721 ifsp = ckzalloc(sizeof(*ifsp));
5722 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005723 ifslastp->next = ifsp;
5724 INT_ON;
5725 }
5726 ifslastp = ifsp;
5727 ifslastp->begoff = start;
5728 ifslastp->endoff = end;
5729 ifslastp->nulonly = nulonly;
5730}
5731
5732static void
5733removerecordregions(int endoff)
5734{
5735 if (ifslastp == NULL)
5736 return;
5737
5738 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005739 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005740 struct ifsregion *ifsp;
5741 INT_OFF;
5742 ifsp = ifsfirst.next->next;
5743 free(ifsfirst.next);
5744 ifsfirst.next = ifsp;
5745 INT_ON;
5746 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005747 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005748 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005749 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005750 ifslastp = &ifsfirst;
5751 ifsfirst.endoff = endoff;
5752 }
5753 return;
5754 }
5755
5756 ifslastp = &ifsfirst;
5757 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005758 ifslastp = ifslastp->next;
5759 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005760 struct ifsregion *ifsp;
5761 INT_OFF;
5762 ifsp = ifslastp->next->next;
5763 free(ifslastp->next);
5764 ifslastp->next = ifsp;
5765 INT_ON;
5766 }
5767 if (ifslastp->endoff > endoff)
5768 ifslastp->endoff = endoff;
5769}
5770
5771static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005772exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005773{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005774 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005775 char *name;
5776 struct passwd *pw;
5777 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005778 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005779 int startloc;
5780
5781 name = p + 1;
5782
5783 while ((c = *++p) != '\0') {
5784 switch (c) {
5785 case CTLESC:
5786 return startp;
5787 case CTLQUOTEMARK:
5788 return startp;
5789 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005790 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005791 goto done;
5792 break;
5793 case '/':
5794 case CTLENDVAR:
5795 goto done;
5796 }
5797 }
5798 done:
5799 *p = '\0';
5800 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005801 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005802 } else {
5803 pw = getpwnam(name);
5804 if (pw == NULL)
5805 goto lose;
5806 home = pw->pw_dir;
5807 }
5808 if (!home || !*home)
5809 goto lose;
5810 *p = c;
5811 startloc = expdest - (char *)stackblock();
5812 strtodest(home, SQSYNTAX, quotes);
5813 recordregion(startloc, expdest - (char *)stackblock(), 0);
5814 return p;
5815 lose:
5816 *p = c;
5817 return startp;
5818}
5819
5820/*
5821 * Execute a command inside back quotes. If it's a builtin command, we
5822 * want to save its output in a block obtained from malloc. Otherwise
5823 * we fork off a subprocess and get the output of the command via a pipe.
5824 * Should be called with interrupts off.
5825 */
5826struct backcmd { /* result of evalbackcmd */
5827 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005828 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005829 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005830 struct job *jp; /* job structure for command */
5831};
5832
5833/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005834static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005835#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005836static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005837
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005838static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005839evalbackcmd(union node *n, struct backcmd *result)
5840{
5841 int saveherefd;
5842
5843 result->fd = -1;
5844 result->buf = NULL;
5845 result->nleft = 0;
5846 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005847 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005848 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005849
5850 saveherefd = herefd;
5851 herefd = -1;
5852
5853 {
5854 int pip[2];
5855 struct job *jp;
5856
5857 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005858 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005859 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5861 FORCE_INT_ON;
5862 close(pip[0]);
5863 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005864 /*close(1);*/
5865 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 close(pip[1]);
5867 }
5868 eflag = 0;
5869 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5870 /* NOTREACHED */
5871 }
5872 close(pip[1]);
5873 result->fd = pip[0];
5874 result->jp = jp;
5875 }
5876 herefd = saveherefd;
5877 out:
5878 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5879 result->fd, result->buf, result->nleft, result->jp));
5880}
5881
5882/*
5883 * Expand stuff in backwards quotes.
5884 */
5885static void
5886expbackq(union node *cmd, int quoted, int quotes)
5887{
5888 struct backcmd in;
5889 int i;
5890 char buf[128];
5891 char *p;
5892 char *dest;
5893 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005894 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005895 struct stackmark smark;
5896
5897 INT_OFF;
5898 setstackmark(&smark);
5899 dest = expdest;
5900 startloc = dest - (char *)stackblock();
5901 grabstackstr(dest);
5902 evalbackcmd(cmd, &in);
5903 popstackmark(&smark);
5904
5905 p = in.buf;
5906 i = in.nleft;
5907 if (i == 0)
5908 goto read;
5909 for (;;) {
5910 memtodest(p, i, syntax, quotes);
5911 read:
5912 if (in.fd < 0)
5913 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005914 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005915 TRACE(("expbackq: read returns %d\n", i));
5916 if (i <= 0)
5917 break;
5918 p = buf;
5919 }
5920
Denis Vlasenko60818682007-09-28 22:07:23 +00005921 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005922 if (in.fd >= 0) {
5923 close(in.fd);
5924 back_exitstatus = waitforjob(in.jp);
5925 }
5926 INT_ON;
5927
5928 /* Eat all trailing newlines */
5929 dest = expdest;
5930 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5931 STUNPUTC(dest);
5932 expdest = dest;
5933
5934 if (quoted == 0)
5935 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005936 TRACE(("evalbackq: size:%d:'%.*s'\n",
5937 (int)((dest - (char *)stackblock()) - startloc),
5938 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005939 stackblock() + startloc));
5940}
5941
Mike Frysinger98c52642009-04-02 10:02:37 +00005942#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005943/*
5944 * Expand arithmetic expression. Backup to start of expression,
5945 * evaluate, place result in (backed up) result, adjust string position.
5946 */
5947static void
5948expari(int quotes)
5949{
5950 char *p, *start;
5951 int begoff;
5952 int flag;
5953 int len;
5954
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005955 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005956
5957 /*
5958 * This routine is slightly over-complicated for
5959 * efficiency. Next we scan backwards looking for the
5960 * start of arithmetic.
5961 */
5962 start = stackblock();
5963 p = expdest - 1;
5964 *p = '\0';
5965 p--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005966 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005967 int esc;
5968
Denys Vlasenkocd716832009-11-28 22:14:02 +01005969 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005970 p--;
5971#if DEBUG
5972 if (p < start) {
5973 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5974 }
5975#endif
5976 }
5977
5978 esc = esclen(start, p);
5979 if (!(esc % 2)) {
5980 break;
5981 }
5982
5983 p -= esc + 1;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005984 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005985
5986 begoff = p - start;
5987
5988 removerecordregions(begoff);
5989
5990 flag = p[1];
5991
5992 expdest = p;
5993
5994 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005995 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005997 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005998
5999 if (flag != '"')
6000 recordregion(begoff, begoff + len, 0);
6001}
6002#endif
6003
6004/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006005static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006006
6007/*
6008 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6009 * characters to allow for further processing. Otherwise treat
6010 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006011 *
6012 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6013 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6014 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006015 */
6016static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006017argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006018{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006019 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006020 '=',
6021 ':',
6022 CTLQUOTEMARK,
6023 CTLENDVAR,
6024 CTLESC,
6025 CTLVAR,
6026 CTLBACKQ,
6027 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006028#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006029 CTLENDARI,
6030#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006031 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006032 };
6033 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006034 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6035 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036 int inquotes;
6037 size_t length;
6038 int startloc;
6039
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006040 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006041 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006042 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 reject++;
6044 }
6045 inquotes = 0;
6046 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006047 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 char *q;
6049
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006050 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006051 tilde:
6052 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006053 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006054 q++;
6055 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006056 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006057 }
6058 start:
6059 startloc = expdest - (char *)stackblock();
6060 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006061 unsigned char c;
6062
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006063 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006064 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006065 if (c) {
6066 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006067 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006068 ) {
6069 /* c == '=' || c == ':' || c == CTLENDARI */
6070 length++;
6071 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006072 }
6073 if (length > 0) {
6074 int newloc;
6075 expdest = stack_nputstr(p, length, expdest);
6076 newloc = expdest - (char *)stackblock();
6077 if (breakall && !inquotes && newloc > startloc) {
6078 recordregion(startloc, newloc, 0);
6079 }
6080 startloc = newloc;
6081 }
6082 p += length + 1;
6083 length = 0;
6084
6085 switch (c) {
6086 case '\0':
6087 goto breakloop;
6088 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006089 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090 p--;
6091 continue;
6092 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006093 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006094 reject++;
6095 /* fall through */
6096 case ':':
6097 /*
6098 * sort of a hack - expand tildes in variable
6099 * assignments (after the first '=' and after ':'s).
6100 */
6101 if (*--p == '~') {
6102 goto tilde;
6103 }
6104 continue;
6105 }
6106
6107 switch (c) {
6108 case CTLENDVAR: /* ??? */
6109 goto breakloop;
6110 case CTLQUOTEMARK:
6111 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006112 if (!inquotes
6113 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006114 && ( p[4] == (char)CTLQUOTEMARK
6115 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006116 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006117 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006118 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006119 goto start;
6120 }
6121 inquotes = !inquotes;
6122 addquote:
6123 if (quotes) {
6124 p--;
6125 length++;
6126 startloc++;
6127 }
6128 break;
6129 case CTLESC:
6130 startloc++;
6131 length++;
6132 goto addquote;
6133 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006134 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006135 goto start;
6136 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006137 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006138 case CTLBACKQ|CTLQUOTE:
6139 expbackq(argbackq->n, c, quotes);
6140 argbackq = argbackq->next;
6141 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006142#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006143 case CTLENDARI:
6144 p--;
6145 expari(quotes);
6146 goto start;
6147#endif
6148 }
6149 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006150 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006151}
6152
6153static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006154scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6155 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006156{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006157 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006158 char c;
6159
6160 loc = startp;
6161 loc2 = rmesc;
6162 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006163 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006164 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006165
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166 c = *loc2;
6167 if (zero) {
6168 *loc2 = '\0';
6169 s = rmesc;
6170 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006171 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006172
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006173 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006174 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006175 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006176 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006177 loc++;
6178 loc++;
6179 loc2++;
6180 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006181 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182}
6183
6184static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006185scanright(char *startp, char *rmesc, char *rmescend,
6186 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006187{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006188#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6189 int try2optimize = match_at_start;
6190#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006191 int esc = 0;
6192 char *loc;
6193 char *loc2;
6194
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006195 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6196 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6197 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6198 * Logic:
6199 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6200 * and on each iteration they go back two/one char until they reach the beginning.
6201 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6202 */
6203 /* TODO: document in what other circumstances we are called. */
6204
6205 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006206 int match;
6207 char c = *loc2;
6208 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006209 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006210 *loc2 = '\0';
6211 s = rmesc;
6212 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006213 match = pmatch(pattern, s);
6214 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006215 *loc2 = c;
6216 if (match)
6217 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006218#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6219 if (try2optimize) {
6220 /* Maybe we can optimize this:
6221 * if pattern ends with unescaped *, we can avoid checking
6222 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6223 * it wont match truncated "raw_value_of_" strings too.
6224 */
6225 unsigned plen = strlen(pattern);
6226 /* Does it end with "*"? */
6227 if (plen != 0 && pattern[--plen] == '*') {
6228 /* "xxxx*" is not escaped */
6229 /* "xxx\*" is escaped */
6230 /* "xx\\*" is not escaped */
6231 /* "x\\\*" is escaped */
6232 int slashes = 0;
6233 while (plen != 0 && pattern[--plen] == '\\')
6234 slashes++;
6235 if (!(slashes & 1))
6236 break; /* ends with unescaped "*" */
6237 }
6238 try2optimize = 0;
6239 }
6240#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006241 loc--;
6242 if (quotes) {
6243 if (--esc < 0) {
6244 esc = esclen(startp, loc);
6245 }
6246 if (esc % 2) {
6247 esc--;
6248 loc--;
6249 }
6250 }
6251 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006252 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006253}
6254
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006255static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006256static void
6257varunset(const char *end, const char *var, const char *umsg, int varflags)
6258{
6259 const char *msg;
6260 const char *tail;
6261
6262 tail = nullstr;
6263 msg = "parameter not set";
6264 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006265 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006266 if (varflags & VSNUL)
6267 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006268 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006269 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006270 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006271 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006272 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273}
6274
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006275#if ENABLE_ASH_BASH_COMPAT
6276static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006277parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006278{
6279 char *idx, *repl = NULL;
6280 unsigned char c;
6281
Denys Vlasenko16149002010-08-06 22:06:21 +02006282 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006283 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006284 idx = arg;
6285 while (1) {
6286 c = *arg;
6287 if (!c)
6288 break;
6289 if (c == '/') {
6290 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006291 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006292 repl = idx + 1;
6293 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006294 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006295 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006296 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006297 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006298 /*
6299 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6300 * The result is a_\_z_c (not a\_\_z_c)!
6301 *
6302 * Enable debug prints in this function and you'll see:
6303 * ash: arg:'\\b/_\\_z_' varflags:d
6304 * ash: pattern:'\\b' repl:'_\_z_'
6305 * That is, \\b is interpreted as \\b, but \\_ as \_!
6306 * IOW: search pattern and replace string treat backslashes
6307 * differently! That is the reason why we check repl below:
6308 */
6309 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6310 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006311 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006312 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006313 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006314
6315 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006316}
6317#endif /* ENABLE_ASH_BASH_COMPAT */
6318
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006319static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006320subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006321 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006322{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006323 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006324 char *startp;
6325 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006327 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006328 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006329 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006330 int saveherefd = herefd;
6331 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006332 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006333 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006334
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006335 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6336 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006337
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006338 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006339 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6340 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006341 STPUTC('\0', expdest);
6342 herefd = saveherefd;
6343 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006344 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006345
6346 switch (subtype) {
6347 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006348 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006349 amount = startp - expdest;
6350 STADJUST(amount, expdest);
6351 return startp;
6352
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006353 case VSQUESTION:
6354 varunset(p, varname, startp, varflags);
6355 /* NOTREACHED */
6356
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006357#if ENABLE_ASH_BASH_COMPAT
6358 case VSSUBSTR:
6359 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006360 /* Read POS in ${var:POS:LEN} */
6361 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006362 len = str - startp - 1;
6363
6364 /* *loc != '\0', guaranteed by parser */
6365 if (quotes) {
6366 char *ptr;
6367
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006368 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006369 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006370 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006371 len--;
6372 ptr++;
6373 }
6374 }
6375 }
6376 orig_len = len;
6377
6378 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006379 /* ${var::LEN} */
6380 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006381 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006382 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006383 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006384 while (*loc && *loc != ':') {
6385 /* TODO?
6386 * bash complains on: var=qwe; echo ${var:1a:123}
6387 if (!isdigit(*loc))
6388 ash_msg_and_raise_error(msg_illnum, str);
6389 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006390 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006391 }
6392 if (*loc++ == ':') {
6393 len = number(loc);
6394 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006395 }
6396 if (pos >= orig_len) {
6397 pos = 0;
6398 len = 0;
6399 }
6400 if (len > (orig_len - pos))
6401 len = orig_len - pos;
6402
6403 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006404 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006405 str++;
6406 }
6407 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006408 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006409 *loc++ = *str++;
6410 *loc++ = *str++;
6411 }
6412 *loc = '\0';
6413 amount = loc - expdest;
6414 STADJUST(amount, expdest);
6415 return loc;
6416#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006417 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006418
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006419 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006420
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006421 /* We'll comeback here if we grow the stack while handling
6422 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6423 * stack will need rebasing, and we'll need to remove our work
6424 * areas each time
6425 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006426 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006427
6428 amount = expdest - ((char *)stackblock() + resetloc);
6429 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006430 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006431
6432 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006433 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006434 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006435 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006436 if (rmesc != startp) {
6437 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006438 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006439 }
6440 }
6441 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006442 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006444 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006445
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006446#if ENABLE_ASH_BASH_COMPAT
6447 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006448 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006449
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006450 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006451 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006452 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006453 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006454 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006455 }
6456
6457 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006458 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006459 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006460
6461 len = 0;
6462 idx = startp;
6463 end = str - 1;
6464 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006465 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006466 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006467 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006468 if (!loc) {
6469 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006470 char *restart_detect = stackblock();
6471 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006472 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006473 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006474 idx++;
6475 len++;
6476 STPUTC(*idx, expdest);
6477 }
6478 if (stackblock() != restart_detect)
6479 goto restart;
6480 idx++;
6481 len++;
6482 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006483 /* continue; - prone to quadratic behavior, smarter code: */
6484 if (idx >= end)
6485 break;
6486 if (str[0] == '*') {
6487 /* Pattern is "*foo". If "*foo" does not match "long_string",
6488 * it would never match "ong_string" etc, no point in trying.
6489 */
6490 goto skip_matching;
6491 }
6492 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006493 }
6494
6495 if (subtype == VSREPLACEALL) {
6496 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006497 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006498 idx++;
6499 idx++;
6500 rmesc++;
6501 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006502 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006503 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006504 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006505
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006506 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006507 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006508 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006509 if (quotes && *loc == '\\') {
6510 STPUTC(CTLESC, expdest);
6511 len++;
6512 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006513 STPUTC(*loc, expdest);
6514 if (stackblock() != restart_detect)
6515 goto restart;
6516 len++;
6517 }
6518
6519 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006520 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006521 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006522 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006523 STPUTC(*idx, expdest);
6524 if (stackblock() != restart_detect)
6525 goto restart;
6526 len++;
6527 idx++;
6528 }
6529 break;
6530 }
6531 }
6532
6533 /* We've put the replaced text into a buffer at workloc, now
6534 * move it to the right place and adjust the stack.
6535 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006536 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006537 startp = (char *)stackblock() + startloc;
6538 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006539 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006540 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006541 STADJUST(-amount, expdest);
6542 return startp;
6543 }
6544#endif /* ENABLE_ASH_BASH_COMPAT */
6545
6546 subtype -= VSTRIMRIGHT;
6547#if DEBUG
6548 if (subtype < 0 || subtype > 7)
6549 abort();
6550#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006551 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006552 zero = subtype >> 1;
6553 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6554 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6555
6556 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6557 if (loc) {
6558 if (zero) {
6559 memmove(startp, loc, str - loc);
6560 loc = startp + (str - loc) - 1;
6561 }
6562 *loc = '\0';
6563 amount = loc - expdest;
6564 STADJUST(amount, expdest);
6565 }
6566 return loc;
6567}
6568
6569/*
6570 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006571 * name parameter (examples):
6572 * ash -c 'echo $1' name:'1='
6573 * ash -c 'echo $qwe' name:'qwe='
6574 * ash -c 'echo $$' name:'$='
6575 * ash -c 'echo ${$}' name:'$='
6576 * ash -c 'echo ${$##q}' name:'$=q'
6577 * ash -c 'echo ${#$}' name:'$='
6578 * note: examples with bad shell syntax:
6579 * ash -c 'echo ${#$1}' name:'$=1'
6580 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006581 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006582static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006583varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006584{
Mike Frysinger98c52642009-04-02 10:02:37 +00006585 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006586 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006587 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588 int sepq = 0;
6589 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006590 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006591 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006592 int quoted = varflags & VSQUOTE;
6593 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006595 switch (*name) {
6596 case '$':
6597 num = rootpid;
6598 goto numvar;
6599 case '?':
6600 num = exitstatus;
6601 goto numvar;
6602 case '#':
6603 num = shellparam.nparam;
6604 goto numvar;
6605 case '!':
6606 num = backgndpid;
6607 if (num == 0)
6608 return -1;
6609 numvar:
6610 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006611 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006612 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006613 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006614 for (i = NOPTS - 1; i >= 0; i--) {
6615 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006616 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006617 len++;
6618 }
6619 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006620 check_1char_name:
6621#if 0
6622 /* handles cases similar to ${#$1} */
6623 if (name[2] != '\0')
6624 raise_error_syntax("bad substitution");
6625#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006626 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006627 case '@': {
6628 char **ap;
6629 int sep;
6630
6631 if (quoted && (flags & EXP_FULL)) {
6632 /* note: this is not meant as PEOF value */
6633 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006634 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006635 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006636 /* fall through */
6637 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006638 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006639 i = SIT(sep, syntax);
6640 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006641 sepq = 1;
6642 param:
6643 ap = shellparam.p;
6644 if (!ap)
6645 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006646 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006647 size_t partlen;
6648
6649 partlen = strlen(p);
6650 len += partlen;
6651
6652 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6653 memtodest(p, partlen, syntax, quotes);
6654
6655 if (*ap && sep) {
6656 char *q;
6657
6658 len++;
6659 if (subtype == VSPLUS || subtype == VSLENGTH) {
6660 continue;
6661 }
6662 q = expdest;
6663 if (sepq)
6664 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006665 /* note: may put NUL despite sep != 0
6666 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006667 STPUTC(sep, q);
6668 expdest = q;
6669 }
6670 }
6671 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006672 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006673 case '0':
6674 case '1':
6675 case '2':
6676 case '3':
6677 case '4':
6678 case '5':
6679 case '6':
6680 case '7':
6681 case '8':
6682 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006683 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006684 if (num < 0 || num > shellparam.nparam)
6685 return -1;
6686 p = num ? shellparam.p[num - 1] : arg0;
6687 goto value;
6688 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006689 /* NB: name has form "VAR=..." */
6690
6691 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6692 * which should be considered before we check variables. */
6693 if (var_str_list) {
6694 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6695 p = NULL;
6696 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006697 char *str, *eq;
6698 str = var_str_list->text;
6699 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006700 if (!eq) /* stop at first non-assignment */
6701 break;
6702 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006703 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006704 && strncmp(str, name, name_len) == 0
6705 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006706 p = eq;
6707 /* goto value; - WRONG! */
6708 /* think "A=1 A=2 B=$A" */
6709 }
6710 var_str_list = var_str_list->next;
6711 } while (var_str_list);
6712 if (p)
6713 goto value;
6714 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006715 p = lookupvar(name);
6716 value:
6717 if (!p)
6718 return -1;
6719
6720 len = strlen(p);
6721 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6722 memtodest(p, len, syntax, quotes);
6723 return len;
6724 }
6725
6726 if (subtype == VSPLUS || subtype == VSLENGTH)
6727 STADJUST(-len, expdest);
6728 return len;
6729}
6730
6731/*
6732 * Expand a variable, and return a pointer to the next character in the
6733 * input string.
6734 */
6735static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006736evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006737{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006738 char varflags;
6739 char subtype;
6740 char quoted;
6741 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006742 char *var;
6743 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006744 int startloc;
6745 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006746
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006747 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006748 subtype = varflags & VSTYPE;
6749 quoted = varflags & VSQUOTE;
6750 var = p;
6751 easy = (!quoted || (*var == '@' && shellparam.nparam));
6752 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006753 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006754
6755 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006756 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006757 if (varflags & VSNUL)
6758 varlen--;
6759
6760 if (subtype == VSPLUS) {
6761 varlen = -1 - varlen;
6762 goto vsplus;
6763 }
6764
6765 if (subtype == VSMINUS) {
6766 vsplus:
6767 if (varlen < 0) {
6768 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006769 p,
6770 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006771 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006772 );
6773 goto end;
6774 }
6775 if (easy)
6776 goto record;
6777 goto end;
6778 }
6779
6780 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6781 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006782 if (subevalvar(p, var, /* strloc: */ 0,
6783 subtype, startloc, varflags,
6784 /* quotes: */ 0,
6785 var_str_list)
6786 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006787 varflags &= ~VSNUL;
6788 /*
6789 * Remove any recorded regions beyond
6790 * start of variable
6791 */
6792 removerecordregions(startloc);
6793 goto again;
6794 }
6795 goto end;
6796 }
6797 if (easy)
6798 goto record;
6799 goto end;
6800 }
6801
6802 if (varlen < 0 && uflag)
6803 varunset(p, var, 0, 0);
6804
6805 if (subtype == VSLENGTH) {
6806 cvtnum(varlen > 0 ? varlen : 0);
6807 goto record;
6808 }
6809
6810 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006811 if (easy)
6812 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006813 goto end;
6814 }
6815
6816#if DEBUG
6817 switch (subtype) {
6818 case VSTRIMLEFT:
6819 case VSTRIMLEFTMAX:
6820 case VSTRIMRIGHT:
6821 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006822#if ENABLE_ASH_BASH_COMPAT
6823 case VSSUBSTR:
6824 case VSREPLACE:
6825 case VSREPLACEALL:
6826#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006827 break;
6828 default:
6829 abort();
6830 }
6831#endif
6832
6833 if (varlen >= 0) {
6834 /*
6835 * Terminate the string and start recording the pattern
6836 * right after it
6837 */
6838 STPUTC('\0', expdest);
6839 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006840 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006841 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006842//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006843 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006844 var_str_list)
6845 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006846 int amount = expdest - (
6847 (char *)stackblock() + patloc - 1
6848 );
6849 STADJUST(-amount, expdest);
6850 }
6851 /* Remove any recorded regions beyond start of variable */
6852 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006853 record:
6854 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006855 }
6856
6857 end:
6858 if (subtype != VSNORMAL) { /* skip to end of alternative */
6859 int nesting = 1;
6860 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006861 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006862 if (c == CTLESC)
6863 p++;
6864 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6865 if (varlen >= 0)
6866 argbackq = argbackq->next;
6867 } else if (c == CTLVAR) {
6868 if ((*p++ & VSTYPE) != VSNORMAL)
6869 nesting++;
6870 } else if (c == CTLENDVAR) {
6871 if (--nesting == 0)
6872 break;
6873 }
6874 }
6875 }
6876 return p;
6877}
6878
6879/*
6880 * Break the argument string into pieces based upon IFS and add the
6881 * strings to the argument list. The regions of the string to be
6882 * searched for IFS characters have been stored by recordregion.
6883 */
6884static void
6885ifsbreakup(char *string, struct arglist *arglist)
6886{
6887 struct ifsregion *ifsp;
6888 struct strlist *sp;
6889 char *start;
6890 char *p;
6891 char *q;
6892 const char *ifs, *realifs;
6893 int ifsspc;
6894 int nulonly;
6895
6896 start = string;
6897 if (ifslastp != NULL) {
6898 ifsspc = 0;
6899 nulonly = 0;
6900 realifs = ifsset() ? ifsval() : defifs;
6901 ifsp = &ifsfirst;
6902 do {
6903 p = string + ifsp->begoff;
6904 nulonly = ifsp->nulonly;
6905 ifs = nulonly ? nullstr : realifs;
6906 ifsspc = 0;
6907 while (p < string + ifsp->endoff) {
6908 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006909 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006910 p++;
6911 if (!strchr(ifs, *p)) {
6912 p++;
6913 continue;
6914 }
6915 if (!nulonly)
6916 ifsspc = (strchr(defifs, *p) != NULL);
6917 /* Ignore IFS whitespace at start */
6918 if (q == start && ifsspc) {
6919 p++;
6920 start = p;
6921 continue;
6922 }
6923 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006924 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006925 sp->text = start;
6926 *arglist->lastp = sp;
6927 arglist->lastp = &sp->next;
6928 p++;
6929 if (!nulonly) {
6930 for (;;) {
6931 if (p >= string + ifsp->endoff) {
6932 break;
6933 }
6934 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006935 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006936 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006937 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938 p = q;
6939 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006940 }
6941 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006942 if (ifsspc) {
6943 p++;
6944 ifsspc = 0;
6945 } else {
6946 p = q;
6947 break;
6948 }
6949 } else
6950 p++;
6951 }
6952 }
6953 start = p;
6954 } /* while */
6955 ifsp = ifsp->next;
6956 } while (ifsp != NULL);
6957 if (nulonly)
6958 goto add;
6959 }
6960
6961 if (!*start)
6962 return;
6963
6964 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006965 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006966 sp->text = start;
6967 *arglist->lastp = sp;
6968 arglist->lastp = &sp->next;
6969}
6970
6971static void
6972ifsfree(void)
6973{
6974 struct ifsregion *p;
6975
6976 INT_OFF;
6977 p = ifsfirst.next;
6978 do {
6979 struct ifsregion *ifsp;
6980 ifsp = p->next;
6981 free(p);
6982 p = ifsp;
6983 } while (p);
6984 ifslastp = NULL;
6985 ifsfirst.next = NULL;
6986 INT_ON;
6987}
6988
6989/*
6990 * Add a file name to the list.
6991 */
6992static void
6993addfname(const char *name)
6994{
6995 struct strlist *sp;
6996
Denis Vlasenko597906c2008-02-20 16:38:54 +00006997 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006998 sp->text = ststrdup(name);
6999 *exparg.lastp = sp;
7000 exparg.lastp = &sp->next;
7001}
7002
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007003/*
7004 * Do metacharacter (i.e. *, ?, [...]) expansion.
7005 */
7006static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007007expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007008{
7009 char *p;
7010 const char *cp;
7011 char *start;
7012 char *endname;
7013 int metaflag;
7014 struct stat statb;
7015 DIR *dirp;
7016 struct dirent *dp;
7017 int atend;
7018 int matchdot;
7019
7020 metaflag = 0;
7021 start = name;
7022 for (p = name; *p; p++) {
7023 if (*p == '*' || *p == '?')
7024 metaflag = 1;
7025 else if (*p == '[') {
7026 char *q = p + 1;
7027 if (*q == '!')
7028 q++;
7029 for (;;) {
7030 if (*q == '\\')
7031 q++;
7032 if (*q == '/' || *q == '\0')
7033 break;
7034 if (*++q == ']') {
7035 metaflag = 1;
7036 break;
7037 }
7038 }
7039 } else if (*p == '\\')
7040 p++;
7041 else if (*p == '/') {
7042 if (metaflag)
7043 goto out;
7044 start = p + 1;
7045 }
7046 }
7047 out:
7048 if (metaflag == 0) { /* we've reached the end of the file name */
7049 if (enddir != expdir)
7050 metaflag++;
7051 p = name;
7052 do {
7053 if (*p == '\\')
7054 p++;
7055 *enddir++ = *p;
7056 } while (*p++);
7057 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7058 addfname(expdir);
7059 return;
7060 }
7061 endname = p;
7062 if (name < start) {
7063 p = name;
7064 do {
7065 if (*p == '\\')
7066 p++;
7067 *enddir++ = *p++;
7068 } while (p < start);
7069 }
7070 if (enddir == expdir) {
7071 cp = ".";
7072 } else if (enddir == expdir + 1 && *expdir == '/') {
7073 cp = "/";
7074 } else {
7075 cp = expdir;
7076 enddir[-1] = '\0';
7077 }
7078 dirp = opendir(cp);
7079 if (dirp == NULL)
7080 return;
7081 if (enddir != expdir)
7082 enddir[-1] = '/';
7083 if (*endname == 0) {
7084 atend = 1;
7085 } else {
7086 atend = 0;
7087 *endname++ = '\0';
7088 }
7089 matchdot = 0;
7090 p = start;
7091 if (*p == '\\')
7092 p++;
7093 if (*p == '.')
7094 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007095 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007096 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007097 continue;
7098 if (pmatch(start, dp->d_name)) {
7099 if (atend) {
7100 strcpy(enddir, dp->d_name);
7101 addfname(expdir);
7102 } else {
7103 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7104 continue;
7105 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007106 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007107 }
7108 }
7109 }
7110 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007111 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007112 endname[-1] = '/';
7113}
7114
7115static struct strlist *
7116msort(struct strlist *list, int len)
7117{
7118 struct strlist *p, *q = NULL;
7119 struct strlist **lpp;
7120 int half;
7121 int n;
7122
7123 if (len <= 1)
7124 return list;
7125 half = len >> 1;
7126 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007127 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007128 q = p;
7129 p = p->next;
7130 }
7131 q->next = NULL; /* terminate first half of list */
7132 q = msort(list, half); /* sort first half of list */
7133 p = msort(p, len - half); /* sort second half */
7134 lpp = &list;
7135 for (;;) {
7136#if ENABLE_LOCALE_SUPPORT
7137 if (strcoll(p->text, q->text) < 0)
7138#else
7139 if (strcmp(p->text, q->text) < 0)
7140#endif
7141 {
7142 *lpp = p;
7143 lpp = &p->next;
7144 p = *lpp;
7145 if (p == NULL) {
7146 *lpp = q;
7147 break;
7148 }
7149 } else {
7150 *lpp = q;
7151 lpp = &q->next;
7152 q = *lpp;
7153 if (q == NULL) {
7154 *lpp = p;
7155 break;
7156 }
7157 }
7158 }
7159 return list;
7160}
7161
7162/*
7163 * Sort the results of file name expansion. It calculates the number of
7164 * strings to sort and then calls msort (short for merge sort) to do the
7165 * work.
7166 */
7167static struct strlist *
7168expsort(struct strlist *str)
7169{
7170 int len;
7171 struct strlist *sp;
7172
7173 len = 0;
7174 for (sp = str; sp; sp = sp->next)
7175 len++;
7176 return msort(str, len);
7177}
7178
7179static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007180expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007181{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007182 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007183 '*', '?', '[', 0
7184 };
7185 /* TODO - EXP_REDIR */
7186
7187 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007188 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007189 struct strlist **savelastp;
7190 struct strlist *sp;
7191 char *p;
7192
7193 if (fflag)
7194 goto nometa;
7195 if (!strpbrk(str->text, metachars))
7196 goto nometa;
7197 savelastp = exparg.lastp;
7198
7199 INT_OFF;
7200 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7201 {
7202 int i = strlen(str->text);
7203 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7204 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007205 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007206 free(expdir);
7207 if (p != str->text)
7208 free(p);
7209 INT_ON;
7210 if (exparg.lastp == savelastp) {
7211 /*
7212 * no matches
7213 */
7214 nometa:
7215 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007216 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007217 exparg.lastp = &str->next;
7218 } else {
7219 *exparg.lastp = NULL;
7220 *savelastp = sp = expsort(*savelastp);
7221 while (sp->next != NULL)
7222 sp = sp->next;
7223 exparg.lastp = &sp->next;
7224 }
7225 str = str->next;
7226 }
7227}
7228
7229/*
7230 * Perform variable substitution and command substitution on an argument,
7231 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7232 * perform splitting and file name expansion. When arglist is NULL, perform
7233 * here document expansion.
7234 */
7235static void
7236expandarg(union node *arg, struct arglist *arglist, int flag)
7237{
7238 struct strlist *sp;
7239 char *p;
7240
7241 argbackq = arg->narg.backquote;
7242 STARTSTACKSTR(expdest);
7243 ifsfirst.next = NULL;
7244 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007245 argstr(arg->narg.text, flag,
7246 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007247 p = _STPUTC('\0', expdest);
7248 expdest = p - 1;
7249 if (arglist == NULL) {
7250 return; /* here document expanded */
7251 }
7252 p = grabstackstr(p);
7253 exparg.lastp = &exparg.list;
7254 /*
7255 * TODO - EXP_REDIR
7256 */
7257 if (flag & EXP_FULL) {
7258 ifsbreakup(p, &exparg);
7259 *exparg.lastp = NULL;
7260 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007261 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007262 } else {
7263 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007264 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007265 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007266 sp->text = p;
7267 *exparg.lastp = sp;
7268 exparg.lastp = &sp->next;
7269 }
7270 if (ifsfirst.next)
7271 ifsfree();
7272 *exparg.lastp = NULL;
7273 if (exparg.list) {
7274 *arglist->lastp = exparg.list;
7275 arglist->lastp = exparg.lastp;
7276 }
7277}
7278
7279/*
7280 * Expand shell variables and backquotes inside a here document.
7281 */
7282static void
7283expandhere(union node *arg, int fd)
7284{
7285 herefd = fd;
7286 expandarg(arg, (struct arglist *)NULL, 0);
7287 full_write(fd, stackblock(), expdest - (char *)stackblock());
7288}
7289
7290/*
7291 * Returns true if the pattern matches the string.
7292 */
7293static int
7294patmatch(char *pattern, const char *string)
7295{
7296 return pmatch(preglob(pattern, 0, 0), string);
7297}
7298
7299/*
7300 * See if a pattern matches in a case statement.
7301 */
7302static int
7303casematch(union node *pattern, char *val)
7304{
7305 struct stackmark smark;
7306 int result;
7307
7308 setstackmark(&smark);
7309 argbackq = pattern->narg.backquote;
7310 STARTSTACKSTR(expdest);
7311 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007312 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7313 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007314 STACKSTRNUL(expdest);
7315 result = patmatch(stackblock(), val);
7316 popstackmark(&smark);
7317 return result;
7318}
7319
7320
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007321/* ============ find_command */
7322
7323struct builtincmd {
7324 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007325 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007326 /* unsigned flags; */
7327};
7328#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007329/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007330 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007331#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007332#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333
7334struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007335 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007336 union param {
7337 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007338 /* index >= 0 for commands without path (slashes) */
7339 /* (TODO: what exactly does the value mean? PATH position?) */
7340 /* index == -1 for commands with slashes */
7341 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007342 const struct builtincmd *cmd;
7343 struct funcnode *func;
7344 } u;
7345};
7346/* values of cmdtype */
7347#define CMDUNKNOWN -1 /* no entry in table for command */
7348#define CMDNORMAL 0 /* command is an executable program */
7349#define CMDFUNCTION 1 /* command is a shell function */
7350#define CMDBUILTIN 2 /* command is a shell builtin */
7351
7352/* action to find_command() */
7353#define DO_ERR 0x01 /* prints errors */
7354#define DO_ABS 0x02 /* checks absolute paths */
7355#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7356#define DO_ALTPATH 0x08 /* using alternate path */
7357#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7358
7359static void find_command(char *, struct cmdentry *, int, const char *);
7360
7361
7362/* ============ Hashing commands */
7363
7364/*
7365 * When commands are first encountered, they are entered in a hash table.
7366 * This ensures that a full path search will not have to be done for them
7367 * on each invocation.
7368 *
7369 * We should investigate converting to a linear search, even though that
7370 * would make the command name "hash" a misnomer.
7371 */
7372
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007373struct tblentry {
7374 struct tblentry *next; /* next entry in hash chain */
7375 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007376 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007377 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007378 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007379};
7380
Denis Vlasenko01631112007-12-16 17:20:38 +00007381static struct tblentry **cmdtable;
7382#define INIT_G_cmdtable() do { \
7383 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7384} while (0)
7385
7386static int builtinloc = -1; /* index in path of %builtin, or -1 */
7387
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007388
7389static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007390tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007392#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007393 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007394 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007395 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007396 while (*envp)
7397 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007398 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007399 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007400 /* re-exec ourselves with the new arguments */
7401 execve(bb_busybox_exec_path, argv, envp);
7402 /* If they called chroot or otherwise made the binary no longer
7403 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007404 }
7405#endif
7406
7407 repeat:
7408#ifdef SYSV
7409 do {
7410 execve(cmd, argv, envp);
7411 } while (errno == EINTR);
7412#else
7413 execve(cmd, argv, envp);
7414#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007415 if (cmd == (char*) bb_busybox_exec_path) {
7416 /* We already visited ENOEXEC branch below, don't do it again */
7417//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007418 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007419 return;
7420 }
7421 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007422 /* Run "cmd" as a shell script:
7423 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7424 * "If the execve() function fails with ENOEXEC, the shell
7425 * shall execute a command equivalent to having a shell invoked
7426 * with the command name as its first operand,
7427 * with any remaining arguments passed to the new shell"
7428 *
7429 * That is, do not use $SHELL, user's shell, or /bin/sh;
7430 * just call ourselves.
7431 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007432 char **ap;
7433 char **new;
7434
7435 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007436 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007437 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7438 new[0] = (char*) "ash";
7439 new[1] = cmd;
7440 ap = new + 2;
7441 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007442 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007443 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007444 argv = new;
7445 goto repeat;
7446 }
7447}
7448
7449/*
7450 * Exec a program. Never returns. If you change this routine, you may
7451 * have to change the find_command routine as well.
7452 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007453static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007454static void
7455shellexec(char **argv, const char *path, int idx)
7456{
7457 char *cmdname;
7458 int e;
7459 char **envp;
7460 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007461#if ENABLE_FEATURE_SH_STANDALONE
7462 int applet_no = -1;
7463#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007464
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007465 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007466 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007467 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007468#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007469 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007470#endif
7471 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007472 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007473 e = errno;
7474 } else {
7475 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007476 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007477 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007478 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007479 if (errno != ENOENT && errno != ENOTDIR)
7480 e = errno;
7481 }
7482 stunalloc(cmdname);
7483 }
7484 }
7485
7486 /* Map to POSIX errors */
7487 switch (e) {
7488 case EACCES:
7489 exerrno = 126;
7490 break;
7491 case ENOENT:
7492 exerrno = 127;
7493 break;
7494 default:
7495 exerrno = 2;
7496 break;
7497 }
7498 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007499 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7500 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007501 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7502 /* NOTREACHED */
7503}
7504
7505static void
7506printentry(struct tblentry *cmdp)
7507{
7508 int idx;
7509 const char *path;
7510 char *name;
7511
7512 idx = cmdp->param.index;
7513 path = pathval();
7514 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007515 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007516 stunalloc(name);
7517 } while (--idx >= 0);
7518 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7519}
7520
7521/*
7522 * Clear out command entries. The argument specifies the first entry in
7523 * PATH which has changed.
7524 */
7525static void
7526clearcmdentry(int firstchange)
7527{
7528 struct tblentry **tblp;
7529 struct tblentry **pp;
7530 struct tblentry *cmdp;
7531
7532 INT_OFF;
7533 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7534 pp = tblp;
7535 while ((cmdp = *pp) != NULL) {
7536 if ((cmdp->cmdtype == CMDNORMAL &&
7537 cmdp->param.index >= firstchange)
7538 || (cmdp->cmdtype == CMDBUILTIN &&
7539 builtinloc >= firstchange)
7540 ) {
7541 *pp = cmdp->next;
7542 free(cmdp);
7543 } else {
7544 pp = &cmdp->next;
7545 }
7546 }
7547 }
7548 INT_ON;
7549}
7550
7551/*
7552 * Locate a command in the command hash table. If "add" is nonzero,
7553 * add the command to the table if it is not already present. The
7554 * variable "lastcmdentry" is set to point to the address of the link
7555 * pointing to the entry, so that delete_cmd_entry can delete the
7556 * entry.
7557 *
7558 * Interrupts must be off if called with add != 0.
7559 */
7560static struct tblentry **lastcmdentry;
7561
7562static struct tblentry *
7563cmdlookup(const char *name, int add)
7564{
7565 unsigned int hashval;
7566 const char *p;
7567 struct tblentry *cmdp;
7568 struct tblentry **pp;
7569
7570 p = name;
7571 hashval = (unsigned char)*p << 4;
7572 while (*p)
7573 hashval += (unsigned char)*p++;
7574 hashval &= 0x7FFF;
7575 pp = &cmdtable[hashval % CMDTABLESIZE];
7576 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7577 if (strcmp(cmdp->cmdname, name) == 0)
7578 break;
7579 pp = &cmdp->next;
7580 }
7581 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007582 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7583 + strlen(name)
7584 /* + 1 - already done because
7585 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007586 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007587 cmdp->cmdtype = CMDUNKNOWN;
7588 strcpy(cmdp->cmdname, name);
7589 }
7590 lastcmdentry = pp;
7591 return cmdp;
7592}
7593
7594/*
7595 * Delete the command entry returned on the last lookup.
7596 */
7597static void
7598delete_cmd_entry(void)
7599{
7600 struct tblentry *cmdp;
7601
7602 INT_OFF;
7603 cmdp = *lastcmdentry;
7604 *lastcmdentry = cmdp->next;
7605 if (cmdp->cmdtype == CMDFUNCTION)
7606 freefunc(cmdp->param.func);
7607 free(cmdp);
7608 INT_ON;
7609}
7610
7611/*
7612 * Add a new command entry, replacing any existing command entry for
7613 * the same name - except special builtins.
7614 */
7615static void
7616addcmdentry(char *name, struct cmdentry *entry)
7617{
7618 struct tblentry *cmdp;
7619
7620 cmdp = cmdlookup(name, 1);
7621 if (cmdp->cmdtype == CMDFUNCTION) {
7622 freefunc(cmdp->param.func);
7623 }
7624 cmdp->cmdtype = entry->cmdtype;
7625 cmdp->param = entry->u;
7626 cmdp->rehash = 0;
7627}
7628
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007629static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007630hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007631{
7632 struct tblentry **pp;
7633 struct tblentry *cmdp;
7634 int c;
7635 struct cmdentry entry;
7636 char *name;
7637
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007638 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007639 clearcmdentry(0);
7640 return 0;
7641 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007642
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007643 if (*argptr == NULL) {
7644 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7645 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7646 if (cmdp->cmdtype == CMDNORMAL)
7647 printentry(cmdp);
7648 }
7649 }
7650 return 0;
7651 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007652
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007653 c = 0;
7654 while ((name = *argptr) != NULL) {
7655 cmdp = cmdlookup(name, 0);
7656 if (cmdp != NULL
7657 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007658 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7659 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007660 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007661 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662 find_command(name, &entry, DO_ERR, pathval());
7663 if (entry.cmdtype == CMDUNKNOWN)
7664 c = 1;
7665 argptr++;
7666 }
7667 return c;
7668}
7669
7670/*
7671 * Called when a cd is done. Marks all commands so the next time they
7672 * are executed they will be rehashed.
7673 */
7674static void
7675hashcd(void)
7676{
7677 struct tblentry **pp;
7678 struct tblentry *cmdp;
7679
7680 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7681 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007682 if (cmdp->cmdtype == CMDNORMAL
7683 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007684 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007685 && builtinloc > 0)
7686 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007687 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007688 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007689 }
7690 }
7691}
7692
7693/*
7694 * Fix command hash table when PATH changed.
7695 * Called before PATH is changed. The argument is the new value of PATH;
7696 * pathval() still returns the old value at this point.
7697 * Called with interrupts off.
7698 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007699static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007700changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007701{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007702 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007703 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007704 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007705 int idx_bltin;
7706
7707 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007708 firstchange = 9999; /* assume no change */
7709 idx = 0;
7710 idx_bltin = -1;
7711 for (;;) {
7712 if (*old != *new) {
7713 firstchange = idx;
7714 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007715 || (*old == ':' && *new == '\0')
7716 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007717 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007718 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007719 old = new; /* ignore subsequent differences */
7720 }
7721 if (*new == '\0')
7722 break;
7723 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7724 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007725 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007726 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007727 new++;
7728 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007729 }
7730 if (builtinloc < 0 && idx_bltin >= 0)
7731 builtinloc = idx_bltin; /* zap builtins */
7732 if (builtinloc >= 0 && idx_bltin < 0)
7733 firstchange = 0;
7734 clearcmdentry(firstchange);
7735 builtinloc = idx_bltin;
7736}
7737
7738#define TEOF 0
7739#define TNL 1
7740#define TREDIR 2
7741#define TWORD 3
7742#define TSEMI 4
7743#define TBACKGND 5
7744#define TAND 6
7745#define TOR 7
7746#define TPIPE 8
7747#define TLP 9
7748#define TRP 10
7749#define TENDCASE 11
7750#define TENDBQUOTE 12
7751#define TNOT 13
7752#define TCASE 14
7753#define TDO 15
7754#define TDONE 16
7755#define TELIF 17
7756#define TELSE 18
7757#define TESAC 19
7758#define TFI 20
7759#define TFOR 21
7760#define TIF 22
7761#define TIN 23
7762#define TTHEN 24
7763#define TUNTIL 25
7764#define TWHILE 26
7765#define TBEGIN 27
7766#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007767typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007768
7769/* first char is indicating which tokens mark the end of a list */
7770static const char *const tokname_array[] = {
7771 "\1end of file",
7772 "\0newline",
7773 "\0redirection",
7774 "\0word",
7775 "\0;",
7776 "\0&",
7777 "\0&&",
7778 "\0||",
7779 "\0|",
7780 "\0(",
7781 "\1)",
7782 "\1;;",
7783 "\1`",
7784#define KWDOFFSET 13
7785 /* the following are keywords */
7786 "\0!",
7787 "\0case",
7788 "\1do",
7789 "\1done",
7790 "\1elif",
7791 "\1else",
7792 "\1esac",
7793 "\1fi",
7794 "\0for",
7795 "\0if",
7796 "\0in",
7797 "\1then",
7798 "\0until",
7799 "\0while",
7800 "\0{",
7801 "\1}",
7802};
7803
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007804/* Wrapper around strcmp for qsort/bsearch/... */
7805static int
7806pstrcmp(const void *a, const void *b)
7807{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007808 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007809}
7810
7811static const char *const *
7812findkwd(const char *s)
7813{
7814 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007815 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7816 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007817}
7818
7819/*
7820 * Locate and print what a word is...
7821 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007822static int
7823describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007824{
7825 struct cmdentry entry;
7826 struct tblentry *cmdp;
7827#if ENABLE_ASH_ALIAS
7828 const struct alias *ap;
7829#endif
7830 const char *path = pathval();
7831
7832 if (describe_command_verbose) {
7833 out1str(command);
7834 }
7835
7836 /* First look at the keywords */
7837 if (findkwd(command)) {
7838 out1str(describe_command_verbose ? " is a shell keyword" : command);
7839 goto out;
7840 }
7841
7842#if ENABLE_ASH_ALIAS
7843 /* Then look at the aliases */
7844 ap = lookupalias(command, 0);
7845 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007846 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007847 out1str("alias ");
7848 printalias(ap);
7849 return 0;
7850 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007851 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007852 goto out;
7853 }
7854#endif
7855 /* Then check if it is a tracked alias */
7856 cmdp = cmdlookup(command, 0);
7857 if (cmdp != NULL) {
7858 entry.cmdtype = cmdp->cmdtype;
7859 entry.u = cmdp->param;
7860 } else {
7861 /* Finally use brute force */
7862 find_command(command, &entry, DO_ABS, path);
7863 }
7864
7865 switch (entry.cmdtype) {
7866 case CMDNORMAL: {
7867 int j = entry.u.index;
7868 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007869 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007870 p = command;
7871 } else {
7872 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007873 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007874 stunalloc(p);
7875 } while (--j >= 0);
7876 }
7877 if (describe_command_verbose) {
7878 out1fmt(" is%s %s",
7879 (cmdp ? " a tracked alias for" : nullstr), p
7880 );
7881 } else {
7882 out1str(p);
7883 }
7884 break;
7885 }
7886
7887 case CMDFUNCTION:
7888 if (describe_command_verbose) {
7889 out1str(" is a shell function");
7890 } else {
7891 out1str(command);
7892 }
7893 break;
7894
7895 case CMDBUILTIN:
7896 if (describe_command_verbose) {
7897 out1fmt(" is a %sshell builtin",
7898 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7899 "special " : nullstr
7900 );
7901 } else {
7902 out1str(command);
7903 }
7904 break;
7905
7906 default:
7907 if (describe_command_verbose) {
7908 out1str(": not found\n");
7909 }
7910 return 127;
7911 }
7912 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007913 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007914 return 0;
7915}
7916
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007917static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007918typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007919{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007920 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007921 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007922 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007923
Denis Vlasenko46846e22007-05-20 13:08:31 +00007924 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007925 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007926 i++;
7927 verbose = 0;
7928 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007929 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007930 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007931 }
7932 return err;
7933}
7934
7935#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007936static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007937commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007938{
7939 int c;
7940 enum {
7941 VERIFY_BRIEF = 1,
7942 VERIFY_VERBOSE = 2,
7943 } verify = 0;
7944
7945 while ((c = nextopt("pvV")) != '\0')
7946 if (c == 'V')
7947 verify |= VERIFY_VERBOSE;
7948 else if (c == 'v')
7949 verify |= VERIFY_BRIEF;
7950#if DEBUG
7951 else if (c != 'p')
7952 abort();
7953#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007954 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7955 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007956 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007957 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007958
7959 return 0;
7960}
7961#endif
7962
7963
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007964/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007965
Denis Vlasenko340299a2008-11-21 10:36:36 +00007966static int funcblocksize; /* size of structures in function */
7967static int funcstringsize; /* size of strings in node */
7968static void *funcblock; /* block to allocate function from */
7969static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007970
Eric Andersencb57d552001-06-28 07:25:16 +00007971/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007972#define EV_EXIT 01 /* exit after evaluating tree */
7973#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007974#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007975
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007976static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007977 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7978 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7979 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7980 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7981 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7982 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7983 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7984 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7985 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7986 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7987 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7988 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7989 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7990 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7991 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7992 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7993 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007994#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007995 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007996#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007997 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7998 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7999 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8000 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8001 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8002 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8003 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8004 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8005 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008006};
8007
8008static void calcsize(union node *n);
8009
8010static void
8011sizenodelist(struct nodelist *lp)
8012{
8013 while (lp) {
8014 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8015 calcsize(lp->n);
8016 lp = lp->next;
8017 }
8018}
8019
8020static void
8021calcsize(union node *n)
8022{
8023 if (n == NULL)
8024 return;
8025 funcblocksize += nodesize[n->type];
8026 switch (n->type) {
8027 case NCMD:
8028 calcsize(n->ncmd.redirect);
8029 calcsize(n->ncmd.args);
8030 calcsize(n->ncmd.assign);
8031 break;
8032 case NPIPE:
8033 sizenodelist(n->npipe.cmdlist);
8034 break;
8035 case NREDIR:
8036 case NBACKGND:
8037 case NSUBSHELL:
8038 calcsize(n->nredir.redirect);
8039 calcsize(n->nredir.n);
8040 break;
8041 case NAND:
8042 case NOR:
8043 case NSEMI:
8044 case NWHILE:
8045 case NUNTIL:
8046 calcsize(n->nbinary.ch2);
8047 calcsize(n->nbinary.ch1);
8048 break;
8049 case NIF:
8050 calcsize(n->nif.elsepart);
8051 calcsize(n->nif.ifpart);
8052 calcsize(n->nif.test);
8053 break;
8054 case NFOR:
8055 funcstringsize += strlen(n->nfor.var) + 1;
8056 calcsize(n->nfor.body);
8057 calcsize(n->nfor.args);
8058 break;
8059 case NCASE:
8060 calcsize(n->ncase.cases);
8061 calcsize(n->ncase.expr);
8062 break;
8063 case NCLIST:
8064 calcsize(n->nclist.body);
8065 calcsize(n->nclist.pattern);
8066 calcsize(n->nclist.next);
8067 break;
8068 case NDEFUN:
8069 case NARG:
8070 sizenodelist(n->narg.backquote);
8071 funcstringsize += strlen(n->narg.text) + 1;
8072 calcsize(n->narg.next);
8073 break;
8074 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008075#if ENABLE_ASH_BASH_COMPAT
8076 case NTO2:
8077#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008078 case NCLOBBER:
8079 case NFROM:
8080 case NFROMTO:
8081 case NAPPEND:
8082 calcsize(n->nfile.fname);
8083 calcsize(n->nfile.next);
8084 break;
8085 case NTOFD:
8086 case NFROMFD:
8087 calcsize(n->ndup.vname);
8088 calcsize(n->ndup.next);
8089 break;
8090 case NHERE:
8091 case NXHERE:
8092 calcsize(n->nhere.doc);
8093 calcsize(n->nhere.next);
8094 break;
8095 case NNOT:
8096 calcsize(n->nnot.com);
8097 break;
8098 };
8099}
8100
8101static char *
8102nodeckstrdup(char *s)
8103{
8104 char *rtn = funcstring;
8105
8106 strcpy(funcstring, s);
8107 funcstring += strlen(s) + 1;
8108 return rtn;
8109}
8110
8111static union node *copynode(union node *);
8112
8113static struct nodelist *
8114copynodelist(struct nodelist *lp)
8115{
8116 struct nodelist *start;
8117 struct nodelist **lpp;
8118
8119 lpp = &start;
8120 while (lp) {
8121 *lpp = funcblock;
8122 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8123 (*lpp)->n = copynode(lp->n);
8124 lp = lp->next;
8125 lpp = &(*lpp)->next;
8126 }
8127 *lpp = NULL;
8128 return start;
8129}
8130
8131static union node *
8132copynode(union node *n)
8133{
8134 union node *new;
8135
8136 if (n == NULL)
8137 return NULL;
8138 new = funcblock;
8139 funcblock = (char *) funcblock + nodesize[n->type];
8140
8141 switch (n->type) {
8142 case NCMD:
8143 new->ncmd.redirect = copynode(n->ncmd.redirect);
8144 new->ncmd.args = copynode(n->ncmd.args);
8145 new->ncmd.assign = copynode(n->ncmd.assign);
8146 break;
8147 case NPIPE:
8148 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008149 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008150 break;
8151 case NREDIR:
8152 case NBACKGND:
8153 case NSUBSHELL:
8154 new->nredir.redirect = copynode(n->nredir.redirect);
8155 new->nredir.n = copynode(n->nredir.n);
8156 break;
8157 case NAND:
8158 case NOR:
8159 case NSEMI:
8160 case NWHILE:
8161 case NUNTIL:
8162 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8163 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8164 break;
8165 case NIF:
8166 new->nif.elsepart = copynode(n->nif.elsepart);
8167 new->nif.ifpart = copynode(n->nif.ifpart);
8168 new->nif.test = copynode(n->nif.test);
8169 break;
8170 case NFOR:
8171 new->nfor.var = nodeckstrdup(n->nfor.var);
8172 new->nfor.body = copynode(n->nfor.body);
8173 new->nfor.args = copynode(n->nfor.args);
8174 break;
8175 case NCASE:
8176 new->ncase.cases = copynode(n->ncase.cases);
8177 new->ncase.expr = copynode(n->ncase.expr);
8178 break;
8179 case NCLIST:
8180 new->nclist.body = copynode(n->nclist.body);
8181 new->nclist.pattern = copynode(n->nclist.pattern);
8182 new->nclist.next = copynode(n->nclist.next);
8183 break;
8184 case NDEFUN:
8185 case NARG:
8186 new->narg.backquote = copynodelist(n->narg.backquote);
8187 new->narg.text = nodeckstrdup(n->narg.text);
8188 new->narg.next = copynode(n->narg.next);
8189 break;
8190 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008191#if ENABLE_ASH_BASH_COMPAT
8192 case NTO2:
8193#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008194 case NCLOBBER:
8195 case NFROM:
8196 case NFROMTO:
8197 case NAPPEND:
8198 new->nfile.fname = copynode(n->nfile.fname);
8199 new->nfile.fd = n->nfile.fd;
8200 new->nfile.next = copynode(n->nfile.next);
8201 break;
8202 case NTOFD:
8203 case NFROMFD:
8204 new->ndup.vname = copynode(n->ndup.vname);
8205 new->ndup.dupfd = n->ndup.dupfd;
8206 new->ndup.fd = n->ndup.fd;
8207 new->ndup.next = copynode(n->ndup.next);
8208 break;
8209 case NHERE:
8210 case NXHERE:
8211 new->nhere.doc = copynode(n->nhere.doc);
8212 new->nhere.fd = n->nhere.fd;
8213 new->nhere.next = copynode(n->nhere.next);
8214 break;
8215 case NNOT:
8216 new->nnot.com = copynode(n->nnot.com);
8217 break;
8218 };
8219 new->type = n->type;
8220 return new;
8221}
8222
8223/*
8224 * Make a copy of a parse tree.
8225 */
8226static struct funcnode *
8227copyfunc(union node *n)
8228{
8229 struct funcnode *f;
8230 size_t blocksize;
8231
8232 funcblocksize = offsetof(struct funcnode, n);
8233 funcstringsize = 0;
8234 calcsize(n);
8235 blocksize = funcblocksize;
8236 f = ckmalloc(blocksize + funcstringsize);
8237 funcblock = (char *) f + offsetof(struct funcnode, n);
8238 funcstring = (char *) f + blocksize;
8239 copynode(n);
8240 f->count = 0;
8241 return f;
8242}
8243
8244/*
8245 * Define a shell function.
8246 */
8247static void
8248defun(char *name, union node *func)
8249{
8250 struct cmdentry entry;
8251
8252 INT_OFF;
8253 entry.cmdtype = CMDFUNCTION;
8254 entry.u.func = copyfunc(func);
8255 addcmdentry(name, &entry);
8256 INT_ON;
8257}
8258
Denis Vlasenko4b875702009-03-19 13:30:04 +00008259/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008260#define SKIPBREAK (1 << 0)
8261#define SKIPCONT (1 << 1)
8262#define SKIPFUNC (1 << 2)
8263#define SKIPFILE (1 << 3)
8264#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008265static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008266static int skipcount; /* number of levels to skip */
8267static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008268static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008269
Denis Vlasenko4b875702009-03-19 13:30:04 +00008270/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008271static int evalstring(char *s, int mask);
8272
Denis Vlasenko4b875702009-03-19 13:30:04 +00008273/* Called to execute a trap.
8274 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008275 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008276 *
8277 * Perhaps we should avoid entering new trap handlers
8278 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008279 */
8280static int
8281dotrap(void)
8282{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008283 uint8_t *g;
8284 int sig;
8285 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008286
8287 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008288 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008289 xbarrier();
8290
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008291 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008292 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8293 int want_exexit;
8294 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008295
Denis Vlasenko4b875702009-03-19 13:30:04 +00008296 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008297 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008298 t = trap[sig];
8299 /* non-trapped SIGINT is handled separately by raise_interrupt,
8300 * don't upset it by resetting gotsig[SIGINT-1] */
8301 if (sig == SIGINT && !t)
8302 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008303
8304 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008305 *g = 0;
8306 if (!t)
8307 continue;
8308 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008309 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008310 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008311 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008312 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008313 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008314 }
8315
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008316 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008317 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008318}
8319
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008320/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008321static void evalloop(union node *, int);
8322static void evalfor(union node *, int);
8323static void evalcase(union node *, int);
8324static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008325static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008326static void evalpipe(union node *, int);
8327static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008328static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008329static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008330
Eric Andersen62483552001-07-10 06:09:16 +00008331/*
Eric Andersenc470f442003-07-28 09:56:35 +00008332 * Evaluate a parse tree. The value is left in the global variable
8333 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008334 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008335static void
Eric Andersenc470f442003-07-28 09:56:35 +00008336evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008337{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008338 struct jmploc *volatile savehandler = exception_handler;
8339 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008340 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008341 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008342 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008343 int int_level;
8344
8345 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008346
Eric Andersenc470f442003-07-28 09:56:35 +00008347 if (n == NULL) {
8348 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008349 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008350 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008351 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008352
8353 exception_handler = &jmploc;
8354 {
8355 int err = setjmp(jmploc.loc);
8356 if (err) {
8357 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008358 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008359 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8360 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008361 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008362 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008363 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008364 TRACE(("exception %d in evaltree, propagating err=%d\n",
8365 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008366 exception_handler = savehandler;
8367 longjmp(exception_handler->loc, err);
8368 }
8369 }
8370
Eric Andersenc470f442003-07-28 09:56:35 +00008371 switch (n->type) {
8372 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008373#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008374 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008375 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008376 break;
8377#endif
8378 case NNOT:
8379 evaltree(n->nnot.com, EV_TESTED);
8380 status = !exitstatus;
8381 goto setstatus;
8382 case NREDIR:
8383 expredir(n->nredir.redirect);
8384 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8385 if (!status) {
8386 evaltree(n->nredir.n, flags & EV_TESTED);
8387 status = exitstatus;
8388 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008389 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008390 goto setstatus;
8391 case NCMD:
8392 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008393 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008394 if (eflag && !(flags & EV_TESTED))
8395 checkexit = ~0;
8396 goto calleval;
8397 case NFOR:
8398 evalfn = evalfor;
8399 goto calleval;
8400 case NWHILE:
8401 case NUNTIL:
8402 evalfn = evalloop;
8403 goto calleval;
8404 case NSUBSHELL:
8405 case NBACKGND:
8406 evalfn = evalsubshell;
8407 goto calleval;
8408 case NPIPE:
8409 evalfn = evalpipe;
8410 goto checkexit;
8411 case NCASE:
8412 evalfn = evalcase;
8413 goto calleval;
8414 case NAND:
8415 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008416 case NSEMI: {
8417
Eric Andersenc470f442003-07-28 09:56:35 +00008418#if NAND + 1 != NOR
8419#error NAND + 1 != NOR
8420#endif
8421#if NOR + 1 != NSEMI
8422#error NOR + 1 != NSEMI
8423#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008424 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008425 evaltree(
8426 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008427 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008428 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008429 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008430 break;
8431 if (!evalskip) {
8432 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008433 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008434 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008435 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008436 evalfn(n, flags);
8437 break;
8438 }
8439 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008440 }
Eric Andersenc470f442003-07-28 09:56:35 +00008441 case NIF:
8442 evaltree(n->nif.test, EV_TESTED);
8443 if (evalskip)
8444 break;
8445 if (exitstatus == 0) {
8446 n = n->nif.ifpart;
8447 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008448 }
8449 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008450 n = n->nif.elsepart;
8451 goto evaln;
8452 }
8453 goto success;
8454 case NDEFUN:
8455 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008456 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008457 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008458 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008459 exitstatus = status;
8460 break;
8461 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008462
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008463 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008464 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008465
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008466 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008467 /* Order of checks below is important:
8468 * signal handlers trigger before exit caused by "set -e".
8469 */
8470 if (pending_sig && dotrap())
8471 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008472 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008473 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008474
8475 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008476 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008477 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008478 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008479
8480 RESTORE_INT(int_level);
8481 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008482}
8483
Eric Andersenc470f442003-07-28 09:56:35 +00008484#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8485static
8486#endif
8487void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8488
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008489static void
Eric Andersenc470f442003-07-28 09:56:35 +00008490evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008491{
8492 int status;
8493
8494 loopnest++;
8495 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008496 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008497 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008498 int i;
8499
Eric Andersencb57d552001-06-28 07:25:16 +00008500 evaltree(n->nbinary.ch1, EV_TESTED);
8501 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008502 skipping:
8503 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008504 evalskip = 0;
8505 continue;
8506 }
8507 if (evalskip == SKIPBREAK && --skipcount <= 0)
8508 evalskip = 0;
8509 break;
8510 }
Eric Andersenc470f442003-07-28 09:56:35 +00008511 i = exitstatus;
8512 if (n->type != NWHILE)
8513 i = !i;
8514 if (i != 0)
8515 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008516 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008517 status = exitstatus;
8518 if (evalskip)
8519 goto skipping;
8520 }
8521 loopnest--;
8522 exitstatus = status;
8523}
8524
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008525static void
Eric Andersenc470f442003-07-28 09:56:35 +00008526evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008527{
8528 struct arglist arglist;
8529 union node *argp;
8530 struct strlist *sp;
8531 struct stackmark smark;
8532
8533 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008534 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008535 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008536 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008537 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008538 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008539 if (evalskip)
8540 goto out;
8541 }
8542 *arglist.lastp = NULL;
8543
8544 exitstatus = 0;
8545 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008546 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008547 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008548 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008549 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008550 if (evalskip) {
8551 if (evalskip == SKIPCONT && --skipcount <= 0) {
8552 evalskip = 0;
8553 continue;
8554 }
8555 if (evalskip == SKIPBREAK && --skipcount <= 0)
8556 evalskip = 0;
8557 break;
8558 }
8559 }
8560 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008561 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008562 popstackmark(&smark);
8563}
8564
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008565static void
Eric Andersenc470f442003-07-28 09:56:35 +00008566evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008567{
8568 union node *cp;
8569 union node *patp;
8570 struct arglist arglist;
8571 struct stackmark smark;
8572
8573 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008574 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008575 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008576 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008577 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008578 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8579 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008580 if (casematch(patp, arglist.list->text)) {
8581 if (evalskip == 0) {
8582 evaltree(cp->nclist.body, flags);
8583 }
8584 goto out;
8585 }
8586 }
8587 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008588 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008589 popstackmark(&smark);
8590}
8591
Eric Andersenc470f442003-07-28 09:56:35 +00008592/*
8593 * Kick off a subshell to evaluate a tree.
8594 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008595static void
Eric Andersenc470f442003-07-28 09:56:35 +00008596evalsubshell(union node *n, int flags)
8597{
8598 struct job *jp;
8599 int backgnd = (n->type == NBACKGND);
8600 int status;
8601
8602 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008603 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008604 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008605 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008606 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008607 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008608 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008609 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008610 flags |= EV_EXIT;
8611 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008612 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008613 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008614 redirect(n->nredir.redirect, 0);
8615 evaltreenr(n->nredir.n, flags);
8616 /* never returns */
8617 }
8618 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008619 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008620 status = waitforjob(jp);
8621 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008622 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008623}
8624
Eric Andersenc470f442003-07-28 09:56:35 +00008625/*
8626 * Compute the names of the files in a redirection list.
8627 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008628static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008629static void
8630expredir(union node *n)
8631{
8632 union node *redir;
8633
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008634 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008635 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008636
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008637 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008638 fn.lastp = &fn.list;
8639 switch (redir->type) {
8640 case NFROMTO:
8641 case NFROM:
8642 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008643#if ENABLE_ASH_BASH_COMPAT
8644 case NTO2:
8645#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008646 case NCLOBBER:
8647 case NAPPEND:
8648 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008649#if ENABLE_ASH_BASH_COMPAT
8650 store_expfname:
8651#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008652 redir->nfile.expfname = fn.list->text;
8653 break;
8654 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008655 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008656 if (redir->ndup.vname) {
8657 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008658 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008659 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008660#if ENABLE_ASH_BASH_COMPAT
8661//FIXME: we used expandarg with different args!
8662 if (!isdigit_str9(fn.list->text)) {
8663 /* >&file, not >&fd */
8664 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8665 ash_msg_and_raise_error("redir error");
8666 redir->type = NTO2;
8667 goto store_expfname;
8668 }
8669#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008670 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008671 }
8672 break;
8673 }
8674 }
8675}
8676
Eric Andersencb57d552001-06-28 07:25:16 +00008677/*
Eric Andersencb57d552001-06-28 07:25:16 +00008678 * Evaluate a pipeline. All the processes in the pipeline are children
8679 * of the process creating the pipeline. (This differs from some versions
8680 * of the shell, which make the last process in a pipeline the parent
8681 * of all the rest.)
8682 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008683static void
Eric Andersenc470f442003-07-28 09:56:35 +00008684evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008685{
8686 struct job *jp;
8687 struct nodelist *lp;
8688 int pipelen;
8689 int prevfd;
8690 int pip[2];
8691
Eric Andersenc470f442003-07-28 09:56:35 +00008692 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008693 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008694 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008695 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008696 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008697 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008698 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008699 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008700 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008701 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008702 pip[1] = -1;
8703 if (lp->next) {
8704 if (pipe(pip) < 0) {
8705 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008706 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008707 }
8708 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008709 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008710 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008711 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008712 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008713 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008714 if (prevfd > 0) {
8715 dup2(prevfd, 0);
8716 close(prevfd);
8717 }
8718 if (pip[1] > 1) {
8719 dup2(pip[1], 1);
8720 close(pip[1]);
8721 }
Eric Andersenc470f442003-07-28 09:56:35 +00008722 evaltreenr(lp->n, flags);
8723 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008724 }
8725 if (prevfd >= 0)
8726 close(prevfd);
8727 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008728 /* Don't want to trigger debugging */
8729 if (pip[1] != -1)
8730 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008731 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008732 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008733 exitstatus = waitforjob(jp);
8734 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008735 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008736 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008737}
8738
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008739/*
8740 * Controls whether the shell is interactive or not.
8741 */
8742static void
8743setinteractive(int on)
8744{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008745 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008746
8747 if (++on == is_interactive)
8748 return;
8749 is_interactive = on;
8750 setsignal(SIGINT);
8751 setsignal(SIGQUIT);
8752 setsignal(SIGTERM);
8753#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8754 if (is_interactive > 1) {
8755 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008756 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008757
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008758 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008759 /* note: ash and hush share this string */
8760 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008761 "Enter 'help' for a list of built-in commands."
8762 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008763 bb_banner,
8764 "built-in shell (ash)"
8765 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008766 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008767 }
8768 }
8769#endif
8770}
8771
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008772static void
8773optschanged(void)
8774{
8775#if DEBUG
8776 opentrace();
8777#endif
8778 setinteractive(iflag);
8779 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008780#if ENABLE_FEATURE_EDITING_VI
8781 if (viflag)
8782 line_input_state->flags |= VI_MODE;
8783 else
8784 line_input_state->flags &= ~VI_MODE;
8785#else
8786 viflag = 0; /* forcibly keep the option off */
8787#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008788}
8789
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008790static struct localvar *localvars;
8791
8792/*
8793 * Called after a function returns.
8794 * Interrupts must be off.
8795 */
8796static void
8797poplocalvars(void)
8798{
8799 struct localvar *lvp;
8800 struct var *vp;
8801
8802 while ((lvp = localvars) != NULL) {
8803 localvars = lvp->next;
8804 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008805 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008806 if (vp == NULL) { /* $- saved */
8807 memcpy(optlist, lvp->text, sizeof(optlist));
8808 free((char*)lvp->text);
8809 optschanged();
8810 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008811 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008812 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008813 if (vp->var_func)
8814 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008815 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008816 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008817 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008818 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008819 }
8820 free(lvp);
8821 }
8822}
8823
8824static int
8825evalfun(struct funcnode *func, int argc, char **argv, int flags)
8826{
8827 volatile struct shparam saveparam;
8828 struct localvar *volatile savelocalvars;
8829 struct jmploc *volatile savehandler;
8830 struct jmploc jmploc;
8831 int e;
8832
8833 saveparam = shellparam;
8834 savelocalvars = localvars;
8835 e = setjmp(jmploc.loc);
8836 if (e) {
8837 goto funcdone;
8838 }
8839 INT_OFF;
8840 savehandler = exception_handler;
8841 exception_handler = &jmploc;
8842 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008843 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008844 func->count++;
8845 funcnest++;
8846 INT_ON;
8847 shellparam.nparam = argc - 1;
8848 shellparam.p = argv + 1;
8849#if ENABLE_ASH_GETOPTS
8850 shellparam.optind = 1;
8851 shellparam.optoff = -1;
8852#endif
8853 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008854 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008855 INT_OFF;
8856 funcnest--;
8857 freefunc(func);
8858 poplocalvars();
8859 localvars = savelocalvars;
8860 freeparam(&shellparam);
8861 shellparam = saveparam;
8862 exception_handler = savehandler;
8863 INT_ON;
8864 evalskip &= ~SKIPFUNC;
8865 return e;
8866}
8867
Denis Vlasenko131ae172007-02-18 13:00:19 +00008868#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008869static char **
8870parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008871{
8872 char *cp, c;
8873
8874 for (;;) {
8875 cp = *++argv;
8876 if (!cp)
8877 return 0;
8878 if (*cp++ != '-')
8879 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008880 c = *cp++;
8881 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008882 break;
8883 if (c == '-' && !*cp) {
8884 argv++;
8885 break;
8886 }
8887 do {
8888 switch (c) {
8889 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008890 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008891 break;
8892 default:
8893 /* run 'typecmd' for other options */
8894 return 0;
8895 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008896 c = *cp++;
8897 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008898 }
8899 return argv;
8900}
8901#endif
8902
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008903/*
8904 * Make a variable a local variable. When a variable is made local, it's
8905 * value and flags are saved in a localvar structure. The saved values
8906 * will be restored when the shell function returns. We handle the name
8907 * "-" as a special case.
8908 */
8909static void
8910mklocal(char *name)
8911{
8912 struct localvar *lvp;
8913 struct var **vpp;
8914 struct var *vp;
8915
8916 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008917 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008918 if (LONE_DASH(name)) {
8919 char *p;
8920 p = ckmalloc(sizeof(optlist));
8921 lvp->text = memcpy(p, optlist, sizeof(optlist));
8922 vp = NULL;
8923 } else {
8924 char *eq;
8925
8926 vpp = hashvar(name);
8927 vp = *findvar(vpp, name);
8928 eq = strchr(name, '=');
8929 if (vp == NULL) {
8930 if (eq)
8931 setvareq(name, VSTRFIXED);
8932 else
8933 setvar(name, NULL, VSTRFIXED);
8934 vp = *vpp; /* the new variable */
8935 lvp->flags = VUNSET;
8936 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008937 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008938 lvp->flags = vp->flags;
8939 vp->flags |= VSTRFIXED|VTEXTFIXED;
8940 if (eq)
8941 setvareq(name, 0);
8942 }
8943 }
8944 lvp->vp = vp;
8945 lvp->next = localvars;
8946 localvars = lvp;
8947 INT_ON;
8948}
8949
8950/*
8951 * The "local" command.
8952 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008953static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008954localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008955{
8956 char *name;
8957
8958 argv = argptr;
8959 while ((name = *argv++) != NULL) {
8960 mklocal(name);
8961 }
8962 return 0;
8963}
8964
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008965static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008966falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008967{
8968 return 1;
8969}
8970
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008971static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008972truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008973{
8974 return 0;
8975}
8976
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008977static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008978execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008979{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008980 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008981 iflag = 0; /* exit on error */
8982 mflag = 0;
8983 optschanged();
8984 shellexec(argv + 1, pathval(), 0);
8985 }
8986 return 0;
8987}
8988
8989/*
8990 * The return command.
8991 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008992static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008993returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008994{
8995 /*
8996 * If called outside a function, do what ksh does;
8997 * skip the rest of the file.
8998 */
8999 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9000 return argv[1] ? number(argv[1]) : exitstatus;
9001}
9002
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009003/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009004static int breakcmd(int, char **) FAST_FUNC;
9005static int dotcmd(int, char **) FAST_FUNC;
9006static int evalcmd(int, char **) FAST_FUNC;
9007static int exitcmd(int, char **) FAST_FUNC;
9008static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009009#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009010static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009011#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009012#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009013static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009014#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009015#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009016static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009017#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009018static int readcmd(int, char **) FAST_FUNC;
9019static int setcmd(int, char **) FAST_FUNC;
9020static int shiftcmd(int, char **) FAST_FUNC;
9021static int timescmd(int, char **) FAST_FUNC;
9022static int trapcmd(int, char **) FAST_FUNC;
9023static int umaskcmd(int, char **) FAST_FUNC;
9024static int unsetcmd(int, char **) FAST_FUNC;
9025static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009026
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009027#define BUILTIN_NOSPEC "0"
9028#define BUILTIN_SPECIAL "1"
9029#define BUILTIN_REGULAR "2"
9030#define BUILTIN_SPEC_REG "3"
9031#define BUILTIN_ASSIGN "4"
9032#define BUILTIN_SPEC_ASSG "5"
9033#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009034#define BUILTIN_SPEC_REG_ASSG "7"
9035
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009036/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009037#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009038static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009039#endif
9040#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009041static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009042#endif
9043#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009044static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009045#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009046
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009047/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009048static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009049 { BUILTIN_SPEC_REG "." , dotcmd },
9050 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009051#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009052 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009053#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009054 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009055#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009056#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009057#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009058 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009059#endif
9060#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009061 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009062#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009063 { BUILTIN_SPEC_REG "break" , breakcmd },
9064 { BUILTIN_REGULAR "cd" , cdcmd },
9065 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009066#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009067 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009068#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009069 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009072#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009073 { BUILTIN_SPEC_REG "eval" , evalcmd },
9074 { BUILTIN_SPEC_REG "exec" , execcmd },
9075 { BUILTIN_SPEC_REG "exit" , exitcmd },
9076 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9077 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009078#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009079 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009080#endif
9081#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009082 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009083#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009084 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009085#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009086 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009087#endif
9088#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009089 { BUILTIN_REGULAR "jobs" , jobscmd },
9090 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009091#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009092#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009093 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009094#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009095 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009096#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009097 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009098#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009099 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9100 { BUILTIN_REGULAR "read" , readcmd },
9101 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9102 { BUILTIN_SPEC_REG "return" , returncmd },
9103 { BUILTIN_SPEC_REG "set" , setcmd },
9104 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009105#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009106 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009107#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009108#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009109 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009110#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009111 { BUILTIN_SPEC_REG "times" , timescmd },
9112 { BUILTIN_SPEC_REG "trap" , trapcmd },
9113 { BUILTIN_REGULAR "true" , truecmd },
9114 { BUILTIN_NOSPEC "type" , typecmd },
9115 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9116 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009117#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009118 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009119#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009120 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9121 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009122};
9123
Denis Vlasenko80591b02008-03-25 07:49:43 +00009124/* Should match the above table! */
9125#define COMMANDCMD (builtintab + \
9126 2 + \
9127 1 * ENABLE_ASH_BUILTIN_TEST + \
9128 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9129 1 * ENABLE_ASH_ALIAS + \
9130 1 * ENABLE_ASH_JOB_CONTROL + \
9131 3)
9132#define EXECCMD (builtintab + \
9133 2 + \
9134 1 * ENABLE_ASH_BUILTIN_TEST + \
9135 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9136 1 * ENABLE_ASH_ALIAS + \
9137 1 * ENABLE_ASH_JOB_CONTROL + \
9138 3 + \
9139 1 * ENABLE_ASH_CMDCMD + \
9140 1 + \
9141 ENABLE_ASH_BUILTIN_ECHO + \
9142 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009143
9144/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009145 * Search the table of builtin commands.
9146 */
9147static struct builtincmd *
9148find_builtin(const char *name)
9149{
9150 struct builtincmd *bp;
9151
9152 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009153 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009154 pstrcmp
9155 );
9156 return bp;
9157}
9158
9159/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009160 * Execute a simple command.
9161 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009162static int
9163isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009164{
9165 const char *q = endofname(p);
9166 if (p == q)
9167 return 0;
9168 return *q == '=';
9169}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009170static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009171bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009172{
9173 /* Preserve exitstatus of a previous possible redirection
9174 * as POSIX mandates */
9175 return back_exitstatus;
9176}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009177static void
Eric Andersenc470f442003-07-28 09:56:35 +00009178evalcommand(union node *cmd, int flags)
9179{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009180 static const struct builtincmd null_bltin = {
9181 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009182 };
Eric Andersenc470f442003-07-28 09:56:35 +00009183 struct stackmark smark;
9184 union node *argp;
9185 struct arglist arglist;
9186 struct arglist varlist;
9187 char **argv;
9188 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009189 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009190 struct cmdentry cmdentry;
9191 struct job *jp;
9192 char *lastarg;
9193 const char *path;
9194 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009195 int status;
9196 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009197 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009198 smallint cmd_is_exec;
9199 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009200
9201 /* First expand the arguments. */
9202 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9203 setstackmark(&smark);
9204 back_exitstatus = 0;
9205
9206 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009207 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009208 varlist.lastp = &varlist.list;
9209 *varlist.lastp = NULL;
9210 arglist.lastp = &arglist.list;
9211 *arglist.lastp = NULL;
9212
9213 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009214 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009215 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9216 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9217 }
9218
Eric Andersenc470f442003-07-28 09:56:35 +00009219 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9220 struct strlist **spp;
9221
9222 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009223 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009224 expandarg(argp, &arglist, EXP_VARTILDE);
9225 else
9226 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9227
Eric Andersenc470f442003-07-28 09:56:35 +00009228 for (sp = *spp; sp; sp = sp->next)
9229 argc++;
9230 }
9231
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009232 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009233 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009234 TRACE(("evalcommand arg: %s\n", sp->text));
9235 *nargv++ = sp->text;
9236 }
9237 *nargv = NULL;
9238
9239 lastarg = NULL;
9240 if (iflag && funcnest == 0 && argc > 0)
9241 lastarg = nargv[-1];
9242
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009243 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009244 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009245 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009246
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009247 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009248 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9249 struct strlist **spp;
9250 char *p;
9251
9252 spp = varlist.lastp;
9253 expandarg(argp, &varlist, EXP_VARTILDE);
9254
9255 /*
9256 * Modify the command lookup path, if a PATH= assignment
9257 * is present
9258 */
9259 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009260 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009261 path = p;
9262 }
9263
9264 /* Print the command if xflag is set. */
9265 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009266 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009267 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009268
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009269 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009270 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009271 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009272 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009273 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009274 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009275 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009276 }
9277 sp = arglist.list;
9278 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009279 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009280 }
9281
9282 cmd_is_exec = 0;
9283 spclbltin = -1;
9284
9285 /* Now locate the command. */
9286 if (argc) {
9287 const char *oldpath;
9288 int cmd_flag = DO_ERR;
9289
9290 path += 5;
9291 oldpath = path;
9292 for (;;) {
9293 find_command(argv[0], &cmdentry, cmd_flag, path);
9294 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009295 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009296 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009297 goto bail;
9298 }
9299
9300 /* implement bltin and command here */
9301 if (cmdentry.cmdtype != CMDBUILTIN)
9302 break;
9303 if (spclbltin < 0)
9304 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9305 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009306 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009307#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009308 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009309 path = oldpath;
9310 nargv = parse_command_args(argv, &path);
9311 if (!nargv)
9312 break;
9313 argc -= nargv - argv;
9314 argv = nargv;
9315 cmd_flag |= DO_NOFUNC;
9316 } else
9317#endif
9318 break;
9319 }
9320 }
9321
9322 if (status) {
9323 /* We have a redirection error. */
9324 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009325 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009326 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009327 exitstatus = status;
9328 goto out;
9329 }
9330
9331 /* Execute the command. */
9332 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009333 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009334
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009335#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009336/* (1) BUG: if variables are set, we need to fork, or save/restore them
9337 * around run_nofork_applet() call.
9338 * (2) Should this check also be done in forkshell()?
9339 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9340 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009341 /* find_command() encodes applet_no as (-2 - applet_no) */
9342 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009343 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009344 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009345 /* run <applet>_main() */
9346 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009347 break;
9348 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009349#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009350 /* Can we avoid forking off? For example, very last command
9351 * in a script or a subshell does not need forking,
9352 * we can just exec it.
9353 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009354 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009355 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009356 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009357 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009358 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009359 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009360 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009361 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009362 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009363 break;
9364 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009365 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009366 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009367 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009368 }
9369 listsetvar(varlist.list, VEXPORT|VSTACK);
9370 shellexec(argv, path, cmdentry.u.index);
9371 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009372 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009373 case CMDBUILTIN:
9374 cmdenviron = varlist.list;
9375 if (cmdenviron) {
9376 struct strlist *list = cmdenviron;
9377 int i = VNOSET;
9378 if (spclbltin > 0 || argc == 0) {
9379 i = 0;
9380 if (cmd_is_exec && argc > 1)
9381 i = VEXPORT;
9382 }
9383 listsetvar(list, i);
9384 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009385 /* Tight loop with builtins only:
9386 * "while kill -0 $child; do true; done"
9387 * will never exit even if $child died, unless we do this
9388 * to reap the zombie and make kill detect that it's gone: */
9389 dowait(DOWAIT_NONBLOCK, NULL);
9390
Eric Andersenc470f442003-07-28 09:56:35 +00009391 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9392 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009393 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009394 if (i == EXEXIT)
9395 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009396 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009397 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009398 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009399 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009400 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009401 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009402 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009403 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009404 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009405 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009406 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009407 }
9408 break;
9409
9410 case CMDFUNCTION:
9411 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009412 /* See above for the rationale */
9413 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009414 if (evalfun(cmdentry.u.func, argc, argv, flags))
9415 goto raise;
9416 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009417
9418 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009419
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009420 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009421 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009422 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009423 /* dsl: I think this is intended to be used to support
9424 * '_' in 'vi' command mode during line editing...
9425 * However I implemented that within libedit itself.
9426 */
9427 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009428 }
Eric Andersenc470f442003-07-28 09:56:35 +00009429 popstackmark(&smark);
9430}
9431
9432static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009433evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9434{
Eric Andersenc470f442003-07-28 09:56:35 +00009435 char *volatile savecmdname;
9436 struct jmploc *volatile savehandler;
9437 struct jmploc jmploc;
9438 int i;
9439
9440 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009441 i = setjmp(jmploc.loc);
9442 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009443 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009444 savehandler = exception_handler;
9445 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009446 commandname = argv[0];
9447 argptr = argv + 1;
9448 optptr = NULL; /* initialize nextopt */
9449 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009450 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009451 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009452 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009453 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009454 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009455 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009456
9457 return i;
9458}
9459
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009460static int
9461goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009462{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009463 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009464}
9465
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009466
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009467/*
9468 * Search for a command. This is called before we fork so that the
9469 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009470 * the child. The check for "goodname" is an overly conservative
9471 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009472 */
Eric Andersenc470f442003-07-28 09:56:35 +00009473static void
9474prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009475{
9476 struct cmdentry entry;
9477
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009478 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9479 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009480}
9481
Eric Andersencb57d552001-06-28 07:25:16 +00009482
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009483/* ============ Builtin commands
9484 *
9485 * Builtin commands whose functions are closely tied to evaluation
9486 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009487 */
9488
9489/*
Eric Andersencb57d552001-06-28 07:25:16 +00009490 * Handle break and continue commands. Break, continue, and return are
9491 * all handled by setting the evalskip flag. The evaluation routines
9492 * above all check this flag, and if it is set they start skipping
9493 * commands rather than executing them. The variable skipcount is
9494 * the number of loops to break/continue, or the number of function
9495 * levels to return. (The latter is always 1.) It should probably
9496 * be an error to break out of more loops than exist, but it isn't
9497 * in the standard shell so we don't make it one here.
9498 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009499static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009500breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009501{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009502 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009503
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009504 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009505 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009506 if (n > loopnest)
9507 n = loopnest;
9508 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009509 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009510 skipcount = n;
9511 }
9512 return 0;
9513}
9514
Eric Andersenc470f442003-07-28 09:56:35 +00009515
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009516/* ============ input.c
9517 *
Eric Andersen90898442003-08-06 11:20:52 +00009518 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009519 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009520
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009521enum {
9522 INPUT_PUSH_FILE = 1,
9523 INPUT_NOFILE_OK = 2,
9524};
Eric Andersencb57d552001-06-28 07:25:16 +00009525
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009526static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009527/* values of checkkwd variable */
9528#define CHKALIAS 0x1
9529#define CHKKWD 0x2
9530#define CHKNL 0x4
9531
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009532/*
9533 * Push a string back onto the input at this current parsefile level.
9534 * We handle aliases this way.
9535 */
9536#if !ENABLE_ASH_ALIAS
9537#define pushstring(s, ap) pushstring(s)
9538#endif
9539static void
9540pushstring(char *s, struct alias *ap)
9541{
9542 struct strpush *sp;
9543 int len;
9544
9545 len = strlen(s);
9546 INT_OFF;
9547 if (g_parsefile->strpush) {
9548 sp = ckzalloc(sizeof(*sp));
9549 sp->prev = g_parsefile->strpush;
9550 } else {
9551 sp = &(g_parsefile->basestrpush);
9552 }
9553 g_parsefile->strpush = sp;
9554 sp->prev_string = g_parsefile->next_to_pgetc;
9555 sp->prev_left_in_line = g_parsefile->left_in_line;
9556#if ENABLE_ASH_ALIAS
9557 sp->ap = ap;
9558 if (ap) {
9559 ap->flag |= ALIASINUSE;
9560 sp->string = s;
9561 }
9562#endif
9563 g_parsefile->next_to_pgetc = s;
9564 g_parsefile->left_in_line = len;
9565 INT_ON;
9566}
9567
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009568static void
9569popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009570{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009571 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009572
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009573 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009574#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009575 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009576 if (g_parsefile->next_to_pgetc[-1] == ' '
9577 || g_parsefile->next_to_pgetc[-1] == '\t'
9578 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009579 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009580 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009581 if (sp->string != sp->ap->val) {
9582 free(sp->string);
9583 }
9584 sp->ap->flag &= ~ALIASINUSE;
9585 if (sp->ap->flag & ALIASDEAD) {
9586 unalias(sp->ap->name);
9587 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009588 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009589#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009590 g_parsefile->next_to_pgetc = sp->prev_string;
9591 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009592 g_parsefile->strpush = sp->prev;
9593 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009594 free(sp);
9595 INT_ON;
9596}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009597
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009598//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9599//it peeks whether it is &>, and then pushes back both chars.
9600//This function needs to save last *next_to_pgetc to buf[0]
9601//to make two pungetc() reliable. Currently,
9602// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009603static int
9604preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009605{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009606 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009607 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009608
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009609 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009610#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009611 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009612 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9613 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009614 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009615 int timeout = -1;
9616# if ENABLE_ASH_IDLE_TIMEOUT
9617 if (iflag) {
9618 const char *tmout_var = lookupvar("TMOUT");
9619 if (tmout_var) {
9620 timeout = atoi(tmout_var) * 1000;
9621 if (timeout <= 0)
9622 timeout = -1;
9623 }
9624 }
9625# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009626# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009627 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009628# endif
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009629 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009630 if (nr == 0) {
9631 /* Ctrl+C pressed */
9632 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009633 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009634 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009635 raise(SIGINT);
9636 return 1;
9637 }
Eric Andersenc470f442003-07-28 09:56:35 +00009638 goto retry;
9639 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009640 if (nr < 0) {
9641 if (errno == 0) {
9642 /* Ctrl+D pressed */
9643 nr = 0;
9644 }
9645# if ENABLE_ASH_IDLE_TIMEOUT
9646 else if (errno == EAGAIN && timeout > 0) {
9647 printf("\007timed out waiting for input: auto-logout\n");
9648 exitshell();
9649 }
9650# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009651 }
Eric Andersencb57d552001-06-28 07:25:16 +00009652 }
9653#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009654 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009655#endif
9656
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009657#if 0 /* disabled: nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009658 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009659 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009660 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009661 if (flags >= 0 && (flags & O_NONBLOCK)) {
9662 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009663 if (fcntl(0, F_SETFL, flags) >= 0) {
9664 out2str("sh: turning off NDELAY mode\n");
9665 goto retry;
9666 }
9667 }
9668 }
9669 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009670#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009671 return nr;
9672}
9673
9674/*
9675 * Refill the input buffer and return the next input character:
9676 *
9677 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009678 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9679 * or we are reading from a string so we can't refill the buffer,
9680 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009681 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009682 * 4) Process input up to the next newline, deleting nul characters.
9683 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009684//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9685#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009686static int
Eric Andersenc470f442003-07-28 09:56:35 +00009687preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009688{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009689 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009690 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009691
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009692 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009693#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009694 if (g_parsefile->left_in_line == -1
9695 && g_parsefile->strpush->ap
9696 && g_parsefile->next_to_pgetc[-1] != ' '
9697 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009698 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009699 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009700 return PEOA;
9701 }
Eric Andersen2870d962001-07-02 17:27:21 +00009702#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009703 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009704 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009705 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9706 g_parsefile->left_in_line,
9707 g_parsefile->next_to_pgetc,
9708 g_parsefile->next_to_pgetc);
9709 if (--g_parsefile->left_in_line >= 0)
9710 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009711 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009712 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009713 * "pgetc" needs refilling.
9714 */
9715
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009716 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009717 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009718 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009719 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009720 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009721 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009722 /* even in failure keep left_in_line and next_to_pgetc
9723 * in lock step, for correct multi-layer pungetc.
9724 * left_in_line was decremented before preadbuffer(),
9725 * must inc next_to_pgetc: */
9726 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009727 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009728 }
Eric Andersencb57d552001-06-28 07:25:16 +00009729
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009730 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009731 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009732 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009733 again:
9734 more = preadfd();
9735 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009736 /* don't try reading again */
9737 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009738 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009739 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009740 return PEOF;
9741 }
9742 }
9743
Denis Vlasenko727752d2008-11-28 03:41:47 +00009744 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009745 * Set g_parsefile->left_in_line
9746 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009747 * NUL chars are deleted.
9748 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009749 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009750 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009751 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009752
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009753 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009754
Denis Vlasenko727752d2008-11-28 03:41:47 +00009755 c = *q;
9756 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009757 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009758 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009759 q++;
9760 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009761 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009762 break;
9763 }
Eric Andersencb57d552001-06-28 07:25:16 +00009764 }
9765
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009766 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009767 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9768 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009769 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009770 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009771 }
9772 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009773 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009774
Eric Andersencb57d552001-06-28 07:25:16 +00009775 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009776 char save = *q;
9777 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009778 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009779 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009780 }
9781
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009782 pgetc_debug("preadbuffer at %d:%p'%s'",
9783 g_parsefile->left_in_line,
9784 g_parsefile->next_to_pgetc,
9785 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009786 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009787}
9788
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009789#define pgetc_as_macro() \
9790 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009791 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009792 : preadbuffer() \
9793 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009794
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009795static int
9796pgetc(void)
9797{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009798 pgetc_debug("pgetc_fast at %d:%p'%s'",
9799 g_parsefile->left_in_line,
9800 g_parsefile->next_to_pgetc,
9801 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009802 return pgetc_as_macro();
9803}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009804
9805#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009806# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009807#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009808# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009809#endif
9810
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009811#if ENABLE_ASH_ALIAS
9812static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009813pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009814{
9815 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009816 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009817 pgetc_debug("pgetc_fast at %d:%p'%s'",
9818 g_parsefile->left_in_line,
9819 g_parsefile->next_to_pgetc,
9820 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009821 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009822 } while (c == PEOA);
9823 return c;
9824}
9825#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009826# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009827#endif
9828
9829/*
9830 * Read a line from the script.
9831 */
9832static char *
9833pfgets(char *line, int len)
9834{
9835 char *p = line;
9836 int nleft = len;
9837 int c;
9838
9839 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009840 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009841 if (c == PEOF) {
9842 if (p == line)
9843 return NULL;
9844 break;
9845 }
9846 *p++ = c;
9847 if (c == '\n')
9848 break;
9849 }
9850 *p = '\0';
9851 return line;
9852}
9853
Eric Andersenc470f442003-07-28 09:56:35 +00009854/*
9855 * Undo the last call to pgetc. Only one character may be pushed back.
9856 * PEOF may be pushed back.
9857 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009858static void
Eric Andersenc470f442003-07-28 09:56:35 +00009859pungetc(void)
9860{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009861 g_parsefile->left_in_line++;
9862 g_parsefile->next_to_pgetc--;
9863 pgetc_debug("pushed back to %d:%p'%s'",
9864 g_parsefile->left_in_line,
9865 g_parsefile->next_to_pgetc,
9866 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009867}
9868
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009869/*
9870 * To handle the "." command, a stack of input files is used. Pushfile
9871 * adds a new entry to the stack and popfile restores the previous level.
9872 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009873static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009874pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009875{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009876 struct parsefile *pf;
9877
Denis Vlasenko597906c2008-02-20 16:38:54 +00009878 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009879 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009880 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009881 /*pf->strpush = NULL; - ckzalloc did it */
9882 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009883 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009884}
9885
9886static void
9887popfile(void)
9888{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009889 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009890
Denis Vlasenkob012b102007-02-19 22:43:01 +00009891 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009892 if (pf->pf_fd >= 0)
9893 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009894 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009895 while (pf->strpush)
9896 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009897 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009898 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009899 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009900}
9901
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009902/*
9903 * Return to top level.
9904 */
9905static void
9906popallfiles(void)
9907{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009908 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009909 popfile();
9910}
9911
9912/*
9913 * Close the file(s) that the shell is reading commands from. Called
9914 * after a fork is done.
9915 */
9916static void
9917closescript(void)
9918{
9919 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009920 if (g_parsefile->pf_fd > 0) {
9921 close(g_parsefile->pf_fd);
9922 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009923 }
9924}
9925
9926/*
9927 * Like setinputfile, but takes an open file descriptor. Call this with
9928 * interrupts off.
9929 */
9930static void
9931setinputfd(int fd, int push)
9932{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009933 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009934 if (push) {
9935 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009936 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009937 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009938 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009939 if (g_parsefile->buf == NULL)
9940 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009941 g_parsefile->left_in_buffer = 0;
9942 g_parsefile->left_in_line = 0;
9943 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009944}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009945
Eric Andersenc470f442003-07-28 09:56:35 +00009946/*
9947 * Set the input to take input from a file. If push is set, push the
9948 * old input onto the stack first.
9949 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009950static int
9951setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009952{
9953 int fd;
9954 int fd2;
9955
Denis Vlasenkob012b102007-02-19 22:43:01 +00009956 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009957 fd = open(fname, O_RDONLY);
9958 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009959 if (flags & INPUT_NOFILE_OK)
9960 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009961 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009962 }
Eric Andersenc470f442003-07-28 09:56:35 +00009963 if (fd < 10) {
9964 fd2 = copyfd(fd, 10);
9965 close(fd);
9966 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009967 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009968 fd = fd2;
9969 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009970 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009971 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009972 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009973 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009974}
9975
Eric Andersencb57d552001-06-28 07:25:16 +00009976/*
9977 * Like setinputfile, but takes input from a string.
9978 */
Eric Andersenc470f442003-07-28 09:56:35 +00009979static void
9980setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009981{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009982 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009983 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009984 g_parsefile->next_to_pgetc = string;
9985 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009986 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009987 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009988 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009989}
9990
9991
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009992/* ============ mail.c
9993 *
9994 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009995 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009996
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009997#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009998
Eric Andersencb57d552001-06-28 07:25:16 +00009999#define MAXMBOXES 10
10000
Eric Andersenc470f442003-07-28 09:56:35 +000010001/* times of mailboxes */
10002static time_t mailtime[MAXMBOXES];
10003/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010004static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010005
Eric Andersencb57d552001-06-28 07:25:16 +000010006/*
Eric Andersenc470f442003-07-28 09:56:35 +000010007 * Print appropriate message(s) if mail has arrived.
10008 * If mail_var_path_changed is set,
10009 * then the value of MAIL has mail_var_path_changed,
10010 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010011 */
Eric Andersenc470f442003-07-28 09:56:35 +000010012static void
10013chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010014{
Eric Andersencb57d552001-06-28 07:25:16 +000010015 const char *mpath;
10016 char *p;
10017 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010018 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010019 struct stackmark smark;
10020 struct stat statb;
10021
Eric Andersencb57d552001-06-28 07:25:16 +000010022 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010023 mpath = mpathset() ? mpathval() : mailval();
10024 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010025 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010026 if (p == NULL)
10027 break;
10028 if (*p == '\0')
10029 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010030 for (q = p; *q; q++)
10031 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010032#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010033 if (q[-1] != '/')
10034 abort();
10035#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010036 q[-1] = '\0'; /* delete trailing '/' */
10037 if (stat(p, &statb) < 0) {
10038 *mtp = 0;
10039 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010040 }
Eric Andersenc470f442003-07-28 09:56:35 +000010041 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10042 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010043 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010044 pathopt ? pathopt : "you have mail"
10045 );
10046 }
10047 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010048 }
Eric Andersenc470f442003-07-28 09:56:35 +000010049 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010050 popstackmark(&smark);
10051}
Eric Andersencb57d552001-06-28 07:25:16 +000010052
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010053static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010054changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010055{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010056 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010057}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010058
Denis Vlasenko131ae172007-02-18 13:00:19 +000010059#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010060
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010061
10062/* ============ ??? */
10063
Eric Andersencb57d552001-06-28 07:25:16 +000010064/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010065 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010066 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010067static void
10068setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010069{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010070 char **newparam;
10071 char **ap;
10072 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010073
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010074 for (nparam = 0; argv[nparam]; nparam++)
10075 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010076 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10077 while (*argv) {
10078 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010079 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010080 *ap = NULL;
10081 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010082 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010083 shellparam.nparam = nparam;
10084 shellparam.p = newparam;
10085#if ENABLE_ASH_GETOPTS
10086 shellparam.optind = 1;
10087 shellparam.optoff = -1;
10088#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010089}
10090
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010091/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010092 * Process shell options. The global variable argptr contains a pointer
10093 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010094 *
10095 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10096 * For a non-interactive shell, an error condition encountered
10097 * by a special built-in ... shall cause the shell to write a diagnostic message
10098 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010099 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010100 * ...
10101 * Utility syntax error (option or operand error) Shall exit
10102 * ...
10103 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10104 * we see that bash does not do that (set "finishes" with error code 1 instead,
10105 * and shell continues), and people rely on this behavior!
10106 * Testcase:
10107 * set -o barfoo 2>/dev/null
10108 * echo $?
10109 *
10110 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010111 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010112static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010113plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010114{
10115 int i;
10116
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010117 if (name) {
10118 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010119 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010120 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010121 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010122 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010123 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010124 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010125 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010126 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010127 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010128 if (val) {
10129 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10130 } else {
10131 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10132 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010133 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010134 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010135}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010136static void
10137setoption(int flag, int val)
10138{
10139 int i;
10140
10141 for (i = 0; i < NOPTS; i++) {
10142 if (optletters(i) == flag) {
10143 optlist[i] = val;
10144 return;
10145 }
10146 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010147 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010148 /* NOTREACHED */
10149}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010150static int
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010151
Eric Andersenc470f442003-07-28 09:56:35 +000010152options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010153{
10154 char *p;
10155 int val;
10156 int c;
10157
10158 if (cmdline)
10159 minusc = NULL;
10160 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010161 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010162 if (c != '-' && c != '+')
10163 break;
10164 argptr++;
10165 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010166 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010167 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010168 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010169 if (!cmdline) {
10170 /* "-" means turn off -x and -v */
10171 if (p[0] == '\0')
10172 xflag = vflag = 0;
10173 /* "--" means reset params */
10174 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010175 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010176 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010177 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010178 }
Eric Andersencb57d552001-06-28 07:25:16 +000010179 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010180 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010181 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010182 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010183 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010184 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010185 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010186 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010187 /* it already printed err message */
10188 return 1; /* error */
10189 }
Eric Andersencb57d552001-06-28 07:25:16 +000010190 if (*argptr)
10191 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010192 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10193 isloginsh = 1;
10194 /* bash does not accept +-login, we also won't */
10195 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010196 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010197 isloginsh = 1;
10198 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010199 } else {
10200 setoption(c, val);
10201 }
10202 }
10203 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010204 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010205}
10206
Eric Andersencb57d552001-06-28 07:25:16 +000010207/*
Eric Andersencb57d552001-06-28 07:25:16 +000010208 * The shift builtin command.
10209 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010210static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010211shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010212{
10213 int n;
10214 char **ap1, **ap2;
10215
10216 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010217 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010218 n = number(argv[1]);
10219 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010220 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010221 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010222 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010223 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010224 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010225 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010226 }
10227 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010228 while ((*ap2++ = *ap1++) != NULL)
10229 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010230#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010231 shellparam.optind = 1;
10232 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010233#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010234 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010235 return 0;
10236}
10237
Eric Andersencb57d552001-06-28 07:25:16 +000010238/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010239 * POSIX requires that 'set' (but not export or readonly) output the
10240 * variables in lexicographic order - by the locale's collating order (sigh).
10241 * Maybe we could keep them in an ordered balanced binary tree
10242 * instead of hashed lists.
10243 * For now just roll 'em through qsort for printing...
10244 */
10245static int
10246showvars(const char *sep_prefix, int on, int off)
10247{
10248 const char *sep;
10249 char **ep, **epend;
10250
10251 ep = listvars(on, off, &epend);
10252 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10253
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010254 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010255
10256 for (; ep < epend; ep++) {
10257 const char *p;
10258 const char *q;
10259
10260 p = strchrnul(*ep, '=');
10261 q = nullstr;
10262 if (*p)
10263 q = single_quote(++p);
10264 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10265 }
10266 return 0;
10267}
10268
10269/*
Eric Andersencb57d552001-06-28 07:25:16 +000010270 * The set command builtin.
10271 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010272static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010273setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010274{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010275 int retval;
10276
Denis Vlasenko68404f12008-03-17 09:00:54 +000010277 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010278 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010279
Denis Vlasenkob012b102007-02-19 22:43:01 +000010280 INT_OFF;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010281 retval = options(/*cmdline:*/ 0);
10282 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010283 optschanged();
10284 if (*argptr != NULL) {
10285 setparam(argptr);
10286 }
Eric Andersencb57d552001-06-28 07:25:16 +000010287 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010288 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010289 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010290}
10291
Denis Vlasenko131ae172007-02-18 13:00:19 +000010292#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010293static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010294change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010295{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010296 uint32_t t;
10297
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010298 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010299 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010300 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010301 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010302 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010303 vrandom.flags &= ~VNOFUNC;
10304 } else {
10305 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010306 t = strtoul(value, NULL, 10);
10307 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010308 }
Eric Andersenef02f822004-03-11 13:34:24 +000010309}
Eric Andersen16767e22004-03-16 05:14:10 +000010310#endif
10311
Denis Vlasenko131ae172007-02-18 13:00:19 +000010312#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010313static int
Eric Andersenc470f442003-07-28 09:56:35 +000010314getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010315{
10316 char *p, *q;
10317 char c = '?';
10318 int done = 0;
10319 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010320 char s[12];
10321 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010322
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010323 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010324 return 1;
10325 optnext = optfirst + *param_optind - 1;
10326
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010327 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010328 p = NULL;
10329 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010330 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010331 if (p == NULL || *p == '\0') {
10332 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010333 p = *optnext;
10334 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010335 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010336 p = NULL;
10337 done = 1;
10338 goto out;
10339 }
10340 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010341 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010342 goto atend;
10343 }
10344
10345 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010346 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010347 if (*q == '\0') {
10348 if (optstr[0] == ':') {
10349 s[0] = c;
10350 s[1] = '\0';
10351 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010352 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010353 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010354 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010355 }
10356 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010357 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010358 }
10359 if (*++q == ':')
10360 q++;
10361 }
10362
10363 if (*++q == ':') {
10364 if (*p == '\0' && (p = *optnext) == NULL) {
10365 if (optstr[0] == ':') {
10366 s[0] = c;
10367 s[1] = '\0';
10368 err |= setvarsafe("OPTARG", s, 0);
10369 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010370 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010371 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010372 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010373 c = '?';
10374 }
Eric Andersenc470f442003-07-28 09:56:35 +000010375 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010376 }
10377
10378 if (p == *optnext)
10379 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010380 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010381 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010382 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010383 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010384 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010385 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010386 *param_optind = optnext - optfirst + 1;
10387 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010388 err |= setvarsafe("OPTIND", s, VNOFUNC);
10389 s[0] = c;
10390 s[1] = '\0';
10391 err |= setvarsafe(optvar, s, 0);
10392 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010393 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010394 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010395 flush_stdout_stderr();
10396 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010397 }
10398 return done;
10399}
Eric Andersenc470f442003-07-28 09:56:35 +000010400
10401/*
10402 * The getopts builtin. Shellparam.optnext points to the next argument
10403 * to be processed. Shellparam.optptr points to the next character to
10404 * be processed in the current argument. If shellparam.optnext is NULL,
10405 * then it's the first time getopts has been called.
10406 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010407static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010408getoptscmd(int argc, char **argv)
10409{
10410 char **optbase;
10411
10412 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010413 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010414 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010415 optbase = shellparam.p;
10416 if (shellparam.optind > shellparam.nparam + 1) {
10417 shellparam.optind = 1;
10418 shellparam.optoff = -1;
10419 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010420 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010421 optbase = &argv[3];
10422 if (shellparam.optind > argc - 2) {
10423 shellparam.optind = 1;
10424 shellparam.optoff = -1;
10425 }
10426 }
10427
10428 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010429 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010430}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010431#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010432
Eric Andersencb57d552001-06-28 07:25:16 +000010433
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010434/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010435
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010436struct heredoc {
10437 struct heredoc *next; /* next here document in list */
10438 union node *here; /* redirection node */
10439 char *eofmark; /* string indicating end of input */
10440 smallint striptabs; /* if set, strip leading tabs */
10441};
10442
10443static smallint tokpushback; /* last token pushed back */
10444static smallint parsebackquote; /* nonzero if we are inside backquotes */
10445static smallint quoteflag; /* set if (part of) last token was quoted */
10446static token_id_t lasttoken; /* last token read (integer id Txxx) */
10447static struct heredoc *heredoclist; /* list of here documents to read */
10448static char *wordtext; /* text of last word returned by readtoken */
10449static struct nodelist *backquotelist;
10450static union node *redirnode;
10451static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010452
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010453static const char *
10454tokname(char *buf, int tok)
10455{
10456 if (tok < TSEMI)
10457 return tokname_array[tok] + 1;
10458 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10459 return buf;
10460}
10461
10462/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010463 * Called when an unexpected token is read during the parse. The argument
10464 * is the token that is expected, or -1 if more than one type of token can
10465 * occur at this point.
10466 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010467static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010468static void
10469raise_error_unexpected_syntax(int token)
10470{
10471 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010472 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010473 int l;
10474
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010475 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010476 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010477 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010478 raise_error_syntax(msg);
10479 /* NOTREACHED */
10480}
Eric Andersencb57d552001-06-28 07:25:16 +000010481
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010482#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010483
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010484/* parsing is heavily cross-recursive, need these forward decls */
10485static union node *andor(void);
10486static union node *pipeline(void);
10487static union node *parse_command(void);
10488static void parseheredoc(void);
10489static char peektoken(void);
10490static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010491
Eric Andersenc470f442003-07-28 09:56:35 +000010492static union node *
10493list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010494{
10495 union node *n1, *n2, *n3;
10496 int tok;
10497
Eric Andersenc470f442003-07-28 09:56:35 +000010498 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10499 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010500 return NULL;
10501 n1 = NULL;
10502 for (;;) {
10503 n2 = andor();
10504 tok = readtoken();
10505 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010506 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010507 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010508 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010509 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010510 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010511 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010512 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010513 n2 = n3;
10514 }
10515 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010516 }
10517 }
10518 if (n1 == NULL) {
10519 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010520 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010521 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010522 n3->type = NSEMI;
10523 n3->nbinary.ch1 = n1;
10524 n3->nbinary.ch2 = n2;
10525 n1 = n3;
10526 }
10527 switch (tok) {
10528 case TBACKGND:
10529 case TSEMI:
10530 tok = readtoken();
10531 /* fall through */
10532 case TNL:
10533 if (tok == TNL) {
10534 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010535 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010536 return n1;
10537 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010538 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010539 }
Eric Andersenc470f442003-07-28 09:56:35 +000010540 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010541 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010542 return n1;
10543 break;
10544 case TEOF:
10545 if (heredoclist)
10546 parseheredoc();
10547 else
Eric Andersenc470f442003-07-28 09:56:35 +000010548 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010549 return n1;
10550 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010551 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010552 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010553 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010554 return n1;
10555 }
10556 }
10557}
10558
Eric Andersenc470f442003-07-28 09:56:35 +000010559static union node *
10560andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010561{
Eric Andersencb57d552001-06-28 07:25:16 +000010562 union node *n1, *n2, *n3;
10563 int t;
10564
Eric Andersencb57d552001-06-28 07:25:16 +000010565 n1 = pipeline();
10566 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010567 t = readtoken();
10568 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010569 t = NAND;
10570 } else if (t == TOR) {
10571 t = NOR;
10572 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010573 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010574 return n1;
10575 }
Eric Andersenc470f442003-07-28 09:56:35 +000010576 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010577 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010578 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010579 n3->type = t;
10580 n3->nbinary.ch1 = n1;
10581 n3->nbinary.ch2 = n2;
10582 n1 = n3;
10583 }
10584}
10585
Eric Andersenc470f442003-07-28 09:56:35 +000010586static union node *
10587pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010588{
Eric Andersencb57d552001-06-28 07:25:16 +000010589 union node *n1, *n2, *pipenode;
10590 struct nodelist *lp, *prev;
10591 int negate;
10592
10593 negate = 0;
10594 TRACE(("pipeline: entered\n"));
10595 if (readtoken() == TNOT) {
10596 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010597 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010598 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010599 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010600 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010601 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010602 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010603 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010604 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010605 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010606 pipenode->npipe.cmdlist = lp;
10607 lp->n = n1;
10608 do {
10609 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010610 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010611 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010612 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010613 prev->next = lp;
10614 } while (readtoken() == TPIPE);
10615 lp->next = NULL;
10616 n1 = pipenode;
10617 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010618 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010619 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010620 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010621 n2->type = NNOT;
10622 n2->nnot.com = n1;
10623 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010624 }
10625 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010626}
10627
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010628static union node *
10629makename(void)
10630{
10631 union node *n;
10632
Denis Vlasenko597906c2008-02-20 16:38:54 +000010633 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010634 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010635 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010636 n->narg.text = wordtext;
10637 n->narg.backquote = backquotelist;
10638 return n;
10639}
10640
10641static void
10642fixredir(union node *n, const char *text, int err)
10643{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010644 int fd;
10645
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010646 TRACE(("Fix redir %s %d\n", text, err));
10647 if (!err)
10648 n->ndup.vname = NULL;
10649
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010650 fd = bb_strtou(text, NULL, 10);
10651 if (!errno && fd >= 0)
10652 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010653 else if (LONE_DASH(text))
10654 n->ndup.dupfd = -1;
10655 else {
10656 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010657 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010658 n->ndup.vname = makename();
10659 }
10660}
10661
10662/*
10663 * Returns true if the text contains nothing to expand (no dollar signs
10664 * or backquotes).
10665 */
10666static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010667noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010668{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010669 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010670
Denys Vlasenkocd716832009-11-28 22:14:02 +010010671 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010672 if (c == CTLQUOTEMARK)
10673 continue;
10674 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010675 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010676 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010677 return 0;
10678 }
10679 return 1;
10680}
10681
10682static void
10683parsefname(void)
10684{
10685 union node *n = redirnode;
10686
10687 if (readtoken() != TWORD)
10688 raise_error_unexpected_syntax(-1);
10689 if (n->type == NHERE) {
10690 struct heredoc *here = heredoc;
10691 struct heredoc *p;
10692 int i;
10693
10694 if (quoteflag == 0)
10695 n->type = NXHERE;
10696 TRACE(("Here document %d\n", n->type));
10697 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010698 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010699 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010700 here->eofmark = wordtext;
10701 here->next = NULL;
10702 if (heredoclist == NULL)
10703 heredoclist = here;
10704 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010705 for (p = heredoclist; p->next; p = p->next)
10706 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010707 p->next = here;
10708 }
10709 } else if (n->type == NTOFD || n->type == NFROMFD) {
10710 fixredir(n, wordtext, 0);
10711 } else {
10712 n->nfile.fname = makename();
10713 }
10714}
Eric Andersencb57d552001-06-28 07:25:16 +000010715
Eric Andersenc470f442003-07-28 09:56:35 +000010716static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010717simplecmd(void)
10718{
10719 union node *args, **app;
10720 union node *n = NULL;
10721 union node *vars, **vpp;
10722 union node **rpp, *redir;
10723 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010724#if ENABLE_ASH_BASH_COMPAT
10725 smallint double_brackets_flag = 0;
10726#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010727
10728 args = NULL;
10729 app = &args;
10730 vars = NULL;
10731 vpp = &vars;
10732 redir = NULL;
10733 rpp = &redir;
10734
10735 savecheckkwd = CHKALIAS;
10736 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010737 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010738 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010739 t = readtoken();
10740 switch (t) {
10741#if ENABLE_ASH_BASH_COMPAT
10742 case TAND: /* "&&" */
10743 case TOR: /* "||" */
10744 if (!double_brackets_flag) {
10745 tokpushback = 1;
10746 goto out;
10747 }
10748 wordtext = (char *) (t == TAND ? "-a" : "-o");
10749#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010750 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010751 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010752 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010753 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010754 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010755#if ENABLE_ASH_BASH_COMPAT
10756 if (strcmp("[[", wordtext) == 0)
10757 double_brackets_flag = 1;
10758 else if (strcmp("]]", wordtext) == 0)
10759 double_brackets_flag = 0;
10760#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010761 n->narg.backquote = backquotelist;
10762 if (savecheckkwd && isassignment(wordtext)) {
10763 *vpp = n;
10764 vpp = &n->narg.next;
10765 } else {
10766 *app = n;
10767 app = &n->narg.next;
10768 savecheckkwd = 0;
10769 }
10770 break;
10771 case TREDIR:
10772 *rpp = n = redirnode;
10773 rpp = &n->nfile.next;
10774 parsefname(); /* read name of redirection file */
10775 break;
10776 case TLP:
10777 if (args && app == &args->narg.next
10778 && !vars && !redir
10779 ) {
10780 struct builtincmd *bcmd;
10781 const char *name;
10782
10783 /* We have a function */
10784 if (readtoken() != TRP)
10785 raise_error_unexpected_syntax(TRP);
10786 name = n->narg.text;
10787 if (!goodname(name)
10788 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10789 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010790 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010791 }
10792 n->type = NDEFUN;
10793 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10794 n->narg.next = parse_command();
10795 return n;
10796 }
10797 /* fall through */
10798 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010799 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010800 goto out;
10801 }
10802 }
10803 out:
10804 *app = NULL;
10805 *vpp = NULL;
10806 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010807 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010808 n->type = NCMD;
10809 n->ncmd.args = args;
10810 n->ncmd.assign = vars;
10811 n->ncmd.redirect = redir;
10812 return n;
10813}
10814
10815static union node *
10816parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010817{
Eric Andersencb57d552001-06-28 07:25:16 +000010818 union node *n1, *n2;
10819 union node *ap, **app;
10820 union node *cp, **cpp;
10821 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010822 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010823 int t;
10824
10825 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010826 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010827
Eric Andersencb57d552001-06-28 07:25:16 +000010828 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010829 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010830 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010831 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010832 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010833 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010834 n1->type = NIF;
10835 n1->nif.test = list(0);
10836 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010837 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010838 n1->nif.ifpart = list(0);
10839 n2 = n1;
10840 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010841 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010842 n2 = n2->nif.elsepart;
10843 n2->type = NIF;
10844 n2->nif.test = list(0);
10845 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010846 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010847 n2->nif.ifpart = list(0);
10848 }
10849 if (lasttoken == TELSE)
10850 n2->nif.elsepart = list(0);
10851 else {
10852 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010853 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010854 }
Eric Andersenc470f442003-07-28 09:56:35 +000010855 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010856 break;
10857 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010858 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010859 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010860 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010861 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010862 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010863 got = readtoken();
10864 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010865 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010866 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010867 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010868 }
10869 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010870 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010871 break;
10872 }
10873 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010874 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010875 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010876 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010877 n1->type = NFOR;
10878 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010879 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010880 if (readtoken() == TIN) {
10881 app = &ap;
10882 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010883 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010884 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010885 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010886 n2->narg.text = wordtext;
10887 n2->narg.backquote = backquotelist;
10888 *app = n2;
10889 app = &n2->narg.next;
10890 }
10891 *app = NULL;
10892 n1->nfor.args = ap;
10893 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010894 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010895 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010896 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010897 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010898 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010899 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010900 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010901 n1->nfor.args = n2;
10902 /*
10903 * Newline or semicolon here is optional (but note
10904 * that the original Bourne shell only allowed NL).
10905 */
10906 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010907 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010908 }
Eric Andersenc470f442003-07-28 09:56:35 +000010909 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010910 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010911 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010912 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010913 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010914 break;
10915 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010916 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010917 n1->type = NCASE;
10918 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010919 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010920 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010921 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010922 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010923 n2->narg.text = wordtext;
10924 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010925 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010926 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010927 } while (readtoken() == TNL);
10928 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010929 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010930 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010931 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010932 checkkwd = CHKNL | CHKKWD;
10933 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010934 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010935 if (lasttoken == TLP)
10936 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010937 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010938 cp->type = NCLIST;
10939 app = &cp->nclist.pattern;
10940 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010941 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010942 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010943 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010944 ap->narg.text = wordtext;
10945 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010946 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010947 break;
10948 app = &ap->narg.next;
10949 readtoken();
10950 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010951 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010952 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010953 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010954 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010955
Eric Andersenc470f442003-07-28 09:56:35 +000010956 cpp = &cp->nclist.next;
10957
10958 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010959 t = readtoken();
10960 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010961 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010962 raise_error_unexpected_syntax(TENDCASE);
10963 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010964 }
Eric Andersenc470f442003-07-28 09:56:35 +000010965 }
Eric Andersencb57d552001-06-28 07:25:16 +000010966 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010967 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010968 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010969 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010970 n1->type = NSUBSHELL;
10971 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010972 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010973 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010974 break;
10975 case TBEGIN:
10976 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010977 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010978 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010979 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010980 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010981 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010982 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010983 }
10984
Eric Andersenc470f442003-07-28 09:56:35 +000010985 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010986 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010987
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010988 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010989 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010990 checkkwd = CHKKWD | CHKALIAS;
10991 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010992 while (readtoken() == TREDIR) {
10993 *rpp = n2 = redirnode;
10994 rpp = &n2->nfile.next;
10995 parsefname();
10996 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010997 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010998 *rpp = NULL;
10999 if (redir) {
11000 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011001 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011002 n2->type = NREDIR;
11003 n2->nredir.n = n1;
11004 n1 = n2;
11005 }
11006 n1->nredir.redirect = redir;
11007 }
Eric Andersencb57d552001-06-28 07:25:16 +000011008 return n1;
11009}
11010
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011011#if ENABLE_ASH_BASH_COMPAT
11012static int decode_dollar_squote(void)
11013{
11014 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11015 int c, cnt;
11016 char *p;
11017 char buf[4];
11018
11019 c = pgetc();
11020 p = strchr(C_escapes, c);
11021 if (p) {
11022 buf[0] = c;
11023 p = buf;
11024 cnt = 3;
11025 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11026 do {
11027 c = pgetc();
11028 *++p = c;
11029 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11030 pungetc();
11031 } else if (c == 'x') { /* \xHH */
11032 do {
11033 c = pgetc();
11034 *++p = c;
11035 } while (isxdigit(c) && --cnt);
11036 pungetc();
11037 if (cnt == 3) { /* \x but next char is "bad" */
11038 c = 'x';
11039 goto unrecognized;
11040 }
11041 } else { /* simple seq like \\ or \t */
11042 p++;
11043 }
11044 *p = '\0';
11045 p = buf;
11046 c = bb_process_escape_sequence((void*)&p);
11047 } else { /* unrecognized "\z": print both chars unless ' or " */
11048 if (c != '\'' && c != '"') {
11049 unrecognized:
11050 c |= 0x100; /* "please encode \, then me" */
11051 }
11052 }
11053 return c;
11054}
11055#endif
11056
Eric Andersencb57d552001-06-28 07:25:16 +000011057/*
11058 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11059 * is not NULL, read a here document. In the latter case, eofmark is the
11060 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011061 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011062 * is the first character of the input token or document.
11063 *
11064 * Because C does not have internal subroutines, I have simulated them
11065 * using goto's to implement the subroutine linkage. The following macros
11066 * will run code that appears at the end of readtoken1.
11067 */
Eric Andersen2870d962001-07-02 17:27:21 +000011068#define CHECKEND() {goto checkend; checkend_return:;}
11069#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11070#define PARSESUB() {goto parsesub; parsesub_return:;}
11071#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11072#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11073#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011074static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011075readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011076{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011077 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011078 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011079 char *out;
11080 int len;
11081 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011082 struct nodelist *bqlist;
11083 smallint quotef;
11084 smallint dblquote;
11085 smallint oldstyle;
11086 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011087#if ENABLE_ASH_EXPAND_PRMT
11088 smallint pssyntax; /* we are expanding a prompt string */
11089#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011090 int varnest; /* levels of variables expansion */
11091 int arinest; /* levels of arithmetic expansion */
11092 int parenlevel; /* levels of parens in arithmetic */
11093 int dqvarnest; /* levels of variables expansion within double quotes */
11094
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011095 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011096
Eric Andersencb57d552001-06-28 07:25:16 +000011097#if __GNUC__
11098 /* Avoid longjmp clobbering */
11099 (void) &out;
11100 (void) &quotef;
11101 (void) &dblquote;
11102 (void) &varnest;
11103 (void) &arinest;
11104 (void) &parenlevel;
11105 (void) &dqvarnest;
11106 (void) &oldstyle;
11107 (void) &prevsyntax;
11108 (void) &syntax;
11109#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011110 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011111 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011112 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011113 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011114#if ENABLE_ASH_EXPAND_PRMT
11115 pssyntax = (syntax == PSSYNTAX);
11116 if (pssyntax)
11117 syntax = DQSYNTAX;
11118#endif
11119 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011120 varnest = 0;
11121 arinest = 0;
11122 parenlevel = 0;
11123 dqvarnest = 0;
11124
11125 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011126 loop:
11127 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011128 CHECKEND(); /* set c to PEOF if at end of here document */
11129 for (;;) { /* until end of line or end of word */
11130 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11131 switch (SIT(c, syntax)) {
11132 case CNL: /* '\n' */
11133 if (syntax == BASESYNTAX)
11134 goto endword; /* exit outer loop */
11135 USTPUTC(c, out);
11136 g_parsefile->linno++;
11137 setprompt_if(doprompt, 2);
11138 c = pgetc();
11139 goto loop; /* continue outer loop */
11140 case CWORD:
11141 USTPUTC(c, out);
11142 break;
11143 case CCTL:
11144 if (eofmark == NULL || dblquote)
11145 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011146#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011147 if (c == '\\' && bash_dollar_squote) {
11148 c = decode_dollar_squote();
11149 if (c & 0x100) {
11150 USTPUTC('\\', out);
11151 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011152 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011153 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011154#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011155 USTPUTC(c, out);
11156 break;
11157 case CBACK: /* backslash */
11158 c = pgetc_without_PEOA();
11159 if (c == PEOF) {
11160 USTPUTC(CTLESC, out);
11161 USTPUTC('\\', out);
11162 pungetc();
11163 } else if (c == '\n') {
11164 setprompt_if(doprompt, 2);
11165 } else {
11166#if ENABLE_ASH_EXPAND_PRMT
11167 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011168 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011169 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011170 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011171#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011172 /* Backslash is retained if we are in "str" and next char isn't special */
11173 if (dblquote
11174 && c != '\\'
11175 && c != '`'
11176 && c != '$'
11177 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011178 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011179 USTPUTC(CTLESC, out);
11180 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011181 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011182 if (SIT(c, SQSYNTAX) == CCTL)
11183 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011184 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011185 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011186 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011187 break;
11188 case CSQUOTE:
11189 syntax = SQSYNTAX;
11190 quotemark:
11191 if (eofmark == NULL) {
11192 USTPUTC(CTLQUOTEMARK, out);
11193 }
11194 break;
11195 case CDQUOTE:
11196 syntax = DQSYNTAX;
11197 dblquote = 1;
11198 goto quotemark;
11199 case CENDQUOTE:
11200 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11201 if (eofmark != NULL && arinest == 0
11202 && varnest == 0
11203 ) {
11204 USTPUTC(c, out);
11205 } else {
11206 if (dqvarnest == 0) {
11207 syntax = BASESYNTAX;
11208 dblquote = 0;
11209 }
11210 quotef = 1;
11211 goto quotemark;
11212 }
11213 break;
11214 case CVAR: /* '$' */
11215 PARSESUB(); /* parse substitution */
11216 break;
11217 case CENDVAR: /* '}' */
11218 if (varnest > 0) {
11219 varnest--;
11220 if (dqvarnest > 0) {
11221 dqvarnest--;
11222 }
11223 c = CTLENDVAR;
11224 }
11225 USTPUTC(c, out);
11226 break;
11227#if ENABLE_SH_MATH_SUPPORT
11228 case CLP: /* '(' in arithmetic */
11229 parenlevel++;
11230 USTPUTC(c, out);
11231 break;
11232 case CRP: /* ')' in arithmetic */
11233 if (parenlevel > 0) {
11234 parenlevel--;
11235 } else {
11236 if (pgetc() == ')') {
11237 if (--arinest == 0) {
11238 syntax = prevsyntax;
11239 dblquote = (syntax == DQSYNTAX);
11240 c = CTLENDARI;
11241 }
11242 } else {
11243 /*
11244 * unbalanced parens
11245 * (don't 2nd guess - no error)
11246 */
11247 pungetc();
11248 }
11249 }
11250 USTPUTC(c, out);
11251 break;
11252#endif
11253 case CBQUOTE: /* '`' */
11254 PARSEBACKQOLD();
11255 break;
11256 case CENDFILE:
11257 goto endword; /* exit outer loop */
11258 case CIGN:
11259 break;
11260 default:
11261 if (varnest == 0) {
11262#if ENABLE_ASH_BASH_COMPAT
11263 if (c == '&') {
11264 if (pgetc() == '>')
11265 c = 0x100 + '>'; /* flag &> */
11266 pungetc();
11267 }
11268#endif
11269 goto endword; /* exit outer loop */
11270 }
11271 IF_ASH_ALIAS(if (c != PEOA))
11272 USTPUTC(c, out);
11273 }
11274 c = pgetc_fast();
11275 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011276 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011277
Mike Frysinger98c52642009-04-02 10:02:37 +000011278#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011279 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011280 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011281#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011282 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011283 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011284 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011285 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011286 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011287 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011288 }
11289 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011290 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011291 out = stackblock();
11292 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011293 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011294 && quotef == 0
11295 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011296 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011297 PARSEREDIR(); /* passed as params: out, c */
11298 lasttoken = TREDIR;
11299 return lasttoken;
11300 }
11301 /* else: non-number X seen, interpret it
11302 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011303 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011304 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011305 }
11306 quoteflag = quotef;
11307 backquotelist = bqlist;
11308 grabstackblock(len);
11309 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011310 lasttoken = TWORD;
11311 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011312/* end of readtoken routine */
11313
Eric Andersencb57d552001-06-28 07:25:16 +000011314/*
11315 * Check to see whether we are at the end of the here document. When this
11316 * is called, c is set to the first character of the next input line. If
11317 * we are at the end of the here document, this routine sets the c to PEOF.
11318 */
Eric Andersenc470f442003-07-28 09:56:35 +000011319checkend: {
11320 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011321#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011322 if (c == PEOA)
11323 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011324#endif
11325 if (striptabs) {
11326 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011327 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011328 }
Eric Andersenc470f442003-07-28 09:56:35 +000011329 }
11330 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011331 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011332 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011333
Eric Andersenc470f442003-07-28 09:56:35 +000011334 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011335 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11336 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011337 if (*p == '\n' && *q == '\0') {
11338 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011339 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011340 needprompt = doprompt;
11341 } else {
11342 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011343 }
11344 }
11345 }
11346 }
Eric Andersenc470f442003-07-28 09:56:35 +000011347 goto checkend_return;
11348}
Eric Andersencb57d552001-06-28 07:25:16 +000011349
Eric Andersencb57d552001-06-28 07:25:16 +000011350/*
11351 * Parse a redirection operator. The variable "out" points to a string
11352 * specifying the fd to be redirected. The variable "c" contains the
11353 * first character of the redirection operator.
11354 */
Eric Andersenc470f442003-07-28 09:56:35 +000011355parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011356 /* out is already checked to be a valid number or "" */
11357 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011358 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011359
Denis Vlasenko597906c2008-02-20 16:38:54 +000011360 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011361 if (c == '>') {
11362 np->nfile.fd = 1;
11363 c = pgetc();
11364 if (c == '>')
11365 np->type = NAPPEND;
11366 else if (c == '|')
11367 np->type = NCLOBBER;
11368 else if (c == '&')
11369 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011370 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011371 else {
11372 np->type = NTO;
11373 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011374 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011375 }
11376#if ENABLE_ASH_BASH_COMPAT
11377 else if (c == 0x100 + '>') { /* this flags &> redirection */
11378 np->nfile.fd = 1;
11379 pgetc(); /* this is '>', no need to check */
11380 np->type = NTO2;
11381 }
11382#endif
11383 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011384 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011385 c = pgetc();
11386 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011387 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011388 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011389 np = stzalloc(sizeof(struct nhere));
11390 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011391 }
11392 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011393 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011394 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011395 c = pgetc();
11396 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011397 heredoc->striptabs = 1;
11398 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011399 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011400 pungetc();
11401 }
11402 break;
11403
11404 case '&':
11405 np->type = NFROMFD;
11406 break;
11407
11408 case '>':
11409 np->type = NFROMTO;
11410 break;
11411
11412 default:
11413 np->type = NFROM;
11414 pungetc();
11415 break;
11416 }
Eric Andersencb57d552001-06-28 07:25:16 +000011417 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011418 if (fd >= 0)
11419 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011420 redirnode = np;
11421 goto parseredir_return;
11422}
Eric Andersencb57d552001-06-28 07:25:16 +000011423
Eric Andersencb57d552001-06-28 07:25:16 +000011424/*
11425 * Parse a substitution. At this point, we have read the dollar sign
11426 * and nothing else.
11427 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011428
11429/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11430 * (assuming ascii char codes, as the original implementation did) */
11431#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011432 (((unsigned)(c) - 33 < 32) \
11433 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011434parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011435 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011436 int typeloc;
11437 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011438
Eric Andersenc470f442003-07-28 09:56:35 +000011439 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011440 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011441 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011442 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011443#if ENABLE_ASH_BASH_COMPAT
11444 if (c == '\'')
11445 bash_dollar_squote = 1;
11446 else
11447#endif
11448 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011449 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011450 } else if (c == '(') {
11451 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011452 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011453#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011454 PARSEARITH();
11455#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011456 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011457#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011458 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011459 pungetc();
11460 PARSEBACKQNEW();
11461 }
11462 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011463 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011464 USTPUTC(CTLVAR, out);
11465 typeloc = out - (char *)stackblock();
11466 USTPUTC(VSNORMAL, out);
11467 subtype = VSNORMAL;
11468 if (c == '{') {
11469 c = pgetc();
11470 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011471 c = pgetc();
11472 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011473 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011474 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011475 subtype = VSLENGTH; /* ${#VAR} */
11476 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011477 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011478 }
Eric Andersenc470f442003-07-28 09:56:35 +000011479 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011480 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011481 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011482 do {
11483 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011484 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011485 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011486 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011487 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011488 do {
11489 STPUTC(c, out);
11490 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011491 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011492 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011493 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011494 USTPUTC(c, out);
11495 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011496 } else {
11497 badsub:
11498 raise_error_syntax("bad substitution");
11499 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011500 if (c != '}' && subtype == VSLENGTH) {
11501 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011502 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011503 }
Eric Andersencb57d552001-06-28 07:25:16 +000011504
Eric Andersenc470f442003-07-28 09:56:35 +000011505 STPUTC('=', out);
11506 flags = 0;
11507 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011508 /* ${VAR...} but not $VAR or ${#VAR} */
11509 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011510 switch (c) {
11511 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011512 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011513#if ENABLE_ASH_BASH_COMPAT
11514 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011515//TODO: support more general format ${v:EXPR:EXPR},
11516// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011517 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011518 pungetc();
11519 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011520 }
11521#endif
11522 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011523 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011524 default: {
11525 static const char types[] ALIGN1 = "}-+?=";
11526 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011527 if (p == NULL)
11528 goto badsub;
11529 subtype = p - types + VSNORMAL;
11530 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011531 }
Eric Andersenc470f442003-07-28 09:56:35 +000011532 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011533 case '#': {
11534 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011535 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011536 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011537 if (c != cc)
11538 goto do_pungetc;
11539 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011540 break;
11541 }
11542#if ENABLE_ASH_BASH_COMPAT
11543 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011544 /* ${v/[/]pattern/repl} */
11545//TODO: encode pattern and repl separately.
11546// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011547 subtype = VSREPLACE;
11548 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011549 if (c != '/')
11550 goto do_pungetc;
11551 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011552 break;
11553#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011554 }
Eric Andersenc470f442003-07-28 09:56:35 +000011555 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011556 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011557 pungetc();
11558 }
11559 if (dblquote || arinest)
11560 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011561 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011562 if (subtype != VSNORMAL) {
11563 varnest++;
11564 if (dblquote || arinest) {
11565 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011566 }
11567 }
11568 }
Eric Andersenc470f442003-07-28 09:56:35 +000011569 goto parsesub_return;
11570}
Eric Andersencb57d552001-06-28 07:25:16 +000011571
Eric Andersencb57d552001-06-28 07:25:16 +000011572/*
11573 * Called to parse command substitutions. Newstyle is set if the command
11574 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11575 * list of commands (passed by reference), and savelen is the number of
11576 * characters on the top of the stack which must be preserved.
11577 */
Eric Andersenc470f442003-07-28 09:56:35 +000011578parsebackq: {
11579 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011580 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011581 union node *n;
11582 char *volatile str;
11583 struct jmploc jmploc;
11584 struct jmploc *volatile savehandler;
11585 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011586 smallint saveprompt = 0;
11587
Eric Andersencb57d552001-06-28 07:25:16 +000011588#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011589 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011590#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011591 savepbq = parsebackquote;
11592 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011593 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011594 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011595 exception_handler = savehandler;
11596 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011597 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011598 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011599 str = NULL;
11600 savelen = out - (char *)stackblock();
11601 if (savelen > 0) {
11602 str = ckmalloc(savelen);
11603 memcpy(str, stackblock(), savelen);
11604 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011605 savehandler = exception_handler;
11606 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011607 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011608 if (oldstyle) {
11609 /* We must read until the closing backquote, giving special
11610 treatment to some slashes, and then push the string and
11611 reread it as input, interpreting it normally. */
11612 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011613 size_t psavelen;
11614 char *pstr;
11615
Eric Andersenc470f442003-07-28 09:56:35 +000011616 STARTSTACKSTR(pout);
11617 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011618 int pc;
11619
11620 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011621 pc = pgetc();
11622 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011623 case '`':
11624 goto done;
11625
11626 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011627 pc = pgetc();
11628 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011629 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011630 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011631 /*
11632 * If eating a newline, avoid putting
11633 * the newline into the new character
11634 * stream (via the STPUTC after the
11635 * switch).
11636 */
11637 continue;
11638 }
11639 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011640 && (!dblquote || pc != '"')
11641 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011642 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011643 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011644 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011645 break;
11646 }
11647 /* fall through */
11648
11649 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011650 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011651 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011652 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011653
11654 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011655 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011656 needprompt = doprompt;
11657 break;
11658
11659 default:
11660 break;
11661 }
11662 STPUTC(pc, pout);
11663 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011664 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011665 STPUTC('\0', pout);
11666 psavelen = pout - (char *)stackblock();
11667 if (psavelen > 0) {
11668 pstr = grabstackstr(pout);
11669 setinputstring(pstr);
11670 }
11671 }
11672 nlpp = &bqlist;
11673 while (*nlpp)
11674 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011675 *nlpp = stzalloc(sizeof(**nlpp));
11676 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011677 parsebackquote = oldstyle;
11678
11679 if (oldstyle) {
11680 saveprompt = doprompt;
11681 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011682 }
11683
Eric Andersenc470f442003-07-28 09:56:35 +000011684 n = list(2);
11685
11686 if (oldstyle)
11687 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011688 else if (readtoken() != TRP)
11689 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011690
11691 (*nlpp)->n = n;
11692 if (oldstyle) {
11693 /*
11694 * Start reading from old file again, ignoring any pushed back
11695 * tokens left from the backquote parsing
11696 */
11697 popfile();
11698 tokpushback = 0;
11699 }
11700 while (stackblocksize() <= savelen)
11701 growstackblock();
11702 STARTSTACKSTR(out);
11703 if (str) {
11704 memcpy(out, str, savelen);
11705 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011706 INT_OFF;
11707 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011708 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011709 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011710 }
11711 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011712 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011713 if (arinest || dblquote)
11714 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11715 else
11716 USTPUTC(CTLBACKQ, out);
11717 if (oldstyle)
11718 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011719 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011720}
11721
Mike Frysinger98c52642009-04-02 10:02:37 +000011722#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011723/*
11724 * Parse an arithmetic expansion (indicate start of one and set state)
11725 */
Eric Andersenc470f442003-07-28 09:56:35 +000011726parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011727 if (++arinest == 1) {
11728 prevsyntax = syntax;
11729 syntax = ARISYNTAX;
11730 USTPUTC(CTLARI, out);
11731 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011732 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011733 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011734 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011735 } else {
11736 /*
11737 * we collapse embedded arithmetic expansion to
11738 * parenthesis, which should be equivalent
11739 */
11740 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011741 }
Eric Andersenc470f442003-07-28 09:56:35 +000011742 goto parsearith_return;
11743}
11744#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011745
Eric Andersenc470f442003-07-28 09:56:35 +000011746} /* end of readtoken */
11747
Eric Andersencb57d552001-06-28 07:25:16 +000011748/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011749 * Read the next input token.
11750 * If the token is a word, we set backquotelist to the list of cmds in
11751 * backquotes. We set quoteflag to true if any part of the word was
11752 * quoted.
11753 * If the token is TREDIR, then we set redirnode to a structure containing
11754 * the redirection.
11755 * In all cases, the variable startlinno is set to the number of the line
11756 * on which the token starts.
11757 *
11758 * [Change comment: here documents and internal procedures]
11759 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11760 * word parsing code into a separate routine. In this case, readtoken
11761 * doesn't need to have any internal procedures, but parseword does.
11762 * We could also make parseoperator in essence the main routine, and
11763 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011764 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011765#define NEW_xxreadtoken
11766#ifdef NEW_xxreadtoken
11767/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011768static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011769 '\n', '(', ')', /* singles */
11770 '&', '|', ';', /* doubles */
11771 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011772};
Eric Andersencb57d552001-06-28 07:25:16 +000011773
Denis Vlasenko834dee72008-10-07 09:18:30 +000011774#define xxreadtoken_singles 3
11775#define xxreadtoken_doubles 3
11776
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011777static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011778 TNL, TLP, TRP, /* only single occurrence allowed */
11779 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11780 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011781 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011782};
11783
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011784static int
11785xxreadtoken(void)
11786{
11787 int c;
11788
11789 if (tokpushback) {
11790 tokpushback = 0;
11791 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011792 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011793 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011794 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011795 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011796 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011797 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011798 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011799
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011800 if (c == '#') {
11801 while ((c = pgetc()) != '\n' && c != PEOF)
11802 continue;
11803 pungetc();
11804 } else if (c == '\\') {
11805 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011806 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011807 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011808 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011809 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011810 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011811 } else {
11812 const char *p;
11813
11814 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11815 if (c != PEOF) {
11816 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011817 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011818 needprompt = doprompt;
11819 }
11820
11821 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011822 if (p == NULL)
11823 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011824
Denis Vlasenko834dee72008-10-07 09:18:30 +000011825 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11826 int cc = pgetc();
11827 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011828 p += xxreadtoken_doubles + 1;
11829 } else {
11830 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011831#if ENABLE_ASH_BASH_COMPAT
11832 if (c == '&' && cc == '>') /* &> */
11833 break; /* return readtoken1(...) */
11834#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011835 }
11836 }
11837 }
11838 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11839 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011840 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011841 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011842
11843 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011844}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011845#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011846#define RETURN(token) return lasttoken = token
11847static int
11848xxreadtoken(void)
11849{
11850 int c;
11851
11852 if (tokpushback) {
11853 tokpushback = 0;
11854 return lasttoken;
11855 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011856 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011857 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011858 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011859 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011860 switch (c) {
11861 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011862 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011863 continue;
11864 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011865 while ((c = pgetc()) != '\n' && c != PEOF)
11866 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011867 pungetc();
11868 continue;
11869 case '\\':
11870 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011871 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011872 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011873 continue;
11874 }
11875 pungetc();
11876 goto breakloop;
11877 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011878 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011879 needprompt = doprompt;
11880 RETURN(TNL);
11881 case PEOF:
11882 RETURN(TEOF);
11883 case '&':
11884 if (pgetc() == '&')
11885 RETURN(TAND);
11886 pungetc();
11887 RETURN(TBACKGND);
11888 case '|':
11889 if (pgetc() == '|')
11890 RETURN(TOR);
11891 pungetc();
11892 RETURN(TPIPE);
11893 case ';':
11894 if (pgetc() == ';')
11895 RETURN(TENDCASE);
11896 pungetc();
11897 RETURN(TSEMI);
11898 case '(':
11899 RETURN(TLP);
11900 case ')':
11901 RETURN(TRP);
11902 default:
11903 goto breakloop;
11904 }
11905 }
11906 breakloop:
11907 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11908#undef RETURN
11909}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011910#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011911
11912static int
11913readtoken(void)
11914{
11915 int t;
11916#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011917 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011918#endif
11919
11920#if ENABLE_ASH_ALIAS
11921 top:
11922#endif
11923
11924 t = xxreadtoken();
11925
11926 /*
11927 * eat newlines
11928 */
11929 if (checkkwd & CHKNL) {
11930 while (t == TNL) {
11931 parseheredoc();
11932 t = xxreadtoken();
11933 }
11934 }
11935
11936 if (t != TWORD || quoteflag) {
11937 goto out;
11938 }
11939
11940 /*
11941 * check for keywords
11942 */
11943 if (checkkwd & CHKKWD) {
11944 const char *const *pp;
11945
11946 pp = findkwd(wordtext);
11947 if (pp) {
11948 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011949 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011950 goto out;
11951 }
11952 }
11953
11954 if (checkkwd & CHKALIAS) {
11955#if ENABLE_ASH_ALIAS
11956 struct alias *ap;
11957 ap = lookupalias(wordtext, 1);
11958 if (ap != NULL) {
11959 if (*ap->val) {
11960 pushstring(ap->val, ap);
11961 }
11962 goto top;
11963 }
11964#endif
11965 }
11966 out:
11967 checkkwd = 0;
11968#if DEBUG
11969 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011970 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011971 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011972 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011973#endif
11974 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011975}
11976
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011977static char
11978peektoken(void)
11979{
11980 int t;
11981
11982 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011983 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011984 return tokname_array[t][0];
11985}
Eric Andersencb57d552001-06-28 07:25:16 +000011986
11987/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011988 * Read and parse a command. Returns NODE_EOF on end of file.
11989 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011990 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011991static union node *
11992parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011993{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011994 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011995
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011996 tokpushback = 0;
11997 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011998 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011999 needprompt = 0;
12000 t = readtoken();
12001 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012002 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012003 if (t == TNL)
12004 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012005 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012006 return list(1);
12007}
12008
12009/*
12010 * Input any here documents.
12011 */
12012static void
12013parseheredoc(void)
12014{
12015 struct heredoc *here;
12016 union node *n;
12017
12018 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012019 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012020
12021 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012022 setprompt_if(needprompt, 2);
12023 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012024 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012025 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012026 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012027 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012028 n->narg.text = wordtext;
12029 n->narg.backquote = backquotelist;
12030 here->here->nhere.doc = n;
12031 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012032 }
Eric Andersencb57d552001-06-28 07:25:16 +000012033}
12034
12035
12036/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012037 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012038 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012039#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012040static const char *
12041expandstr(const char *ps)
12042{
12043 union node n;
12044
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012045 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12046 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012047 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012048 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012049 popfile();
12050
12051 n.narg.type = NARG;
12052 n.narg.next = NULL;
12053 n.narg.text = wordtext;
12054 n.narg.backquote = backquotelist;
12055
12056 expandarg(&n, NULL, 0);
12057 return stackblock();
12058}
12059#endif
12060
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012061/*
12062 * Execute a command or commands contained in a string.
12063 */
12064static int
12065evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012066{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012067 union node *n;
12068 struct stackmark smark;
12069 int skip;
12070
12071 setinputstring(s);
12072 setstackmark(&smark);
12073
12074 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012075 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012076 evaltree(n, 0);
12077 popstackmark(&smark);
12078 skip = evalskip;
12079 if (skip)
12080 break;
12081 }
12082 popfile();
12083
12084 skip &= mask;
12085 evalskip = skip;
12086 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012087}
12088
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012089/*
12090 * The eval command.
12091 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012092static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012093evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012094{
12095 char *p;
12096 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012097
Denis Vlasenko68404f12008-03-17 09:00:54 +000012098 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012099 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012100 argv += 2;
12101 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012102 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012103 for (;;) {
12104 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012105 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012106 if (p == NULL)
12107 break;
12108 STPUTC(' ', concat);
12109 }
12110 STPUTC('\0', concat);
12111 p = grabstackstr(concat);
12112 }
12113 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012114 }
12115 return exitstatus;
12116}
12117
12118/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012119 * Read and execute commands.
12120 * "Top" is nonzero for the top level command loop;
12121 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012122 */
12123static int
12124cmdloop(int top)
12125{
12126 union node *n;
12127 struct stackmark smark;
12128 int inter;
12129 int numeof = 0;
12130
12131 TRACE(("cmdloop(%d) called\n", top));
12132 for (;;) {
12133 int skip;
12134
12135 setstackmark(&smark);
12136#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012137 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012138 showjobs(stderr, SHOW_CHANGED);
12139#endif
12140 inter = 0;
12141 if (iflag && top) {
12142 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012143 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012144 }
12145 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012146#if DEBUG
12147 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012148 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012149#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012150 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012151 if (!top || numeof >= 50)
12152 break;
12153 if (!stoppedjobs()) {
12154 if (!Iflag)
12155 break;
12156 out2str("\nUse \"exit\" to leave shell.\n");
12157 }
12158 numeof++;
12159 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012160 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12161 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012162 numeof = 0;
12163 evaltree(n, 0);
12164 }
12165 popstackmark(&smark);
12166 skip = evalskip;
12167
12168 if (skip) {
12169 evalskip = 0;
12170 return skip & SKIPEVAL;
12171 }
12172 }
12173 return 0;
12174}
12175
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012176/*
12177 * Take commands from a file. To be compatible we should do a path
12178 * search for the file, which is necessary to find sub-commands.
12179 */
12180static char *
12181find_dot_file(char *name)
12182{
12183 char *fullname;
12184 const char *path = pathval();
12185 struct stat statb;
12186
12187 /* don't try this for absolute or relative paths */
12188 if (strchr(name, '/'))
12189 return name;
12190
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012191 /* IIRC standards do not say whether . is to be searched.
12192 * And it is even smaller this way, making it unconditional for now:
12193 */
12194 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12195 fullname = name;
12196 goto try_cur_dir;
12197 }
12198
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012199 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012200 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012201 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12202 /*
12203 * Don't bother freeing here, since it will
12204 * be freed by the caller.
12205 */
12206 return fullname;
12207 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012208 if (fullname != name)
12209 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012210 }
12211
12212 /* not found in the PATH */
12213 ash_msg_and_raise_error("%s: not found", name);
12214 /* NOTREACHED */
12215}
12216
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012217static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012218dotcmd(int argc, char **argv)
12219{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012220 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012221 struct strlist *sp;
12222 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012223
12224 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012225 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012226
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012227 if (!argv[1]) {
12228 /* bash says: "bash: .: filename argument required" */
12229 return 2; /* bash compat */
12230 }
12231
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012232 /* "false; . empty_file; echo $?" should print 0, not 1: */
12233 exitstatus = 0;
12234
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012235 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012236
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012237 argv += 2;
12238 argc -= 2;
12239 if (argc) { /* argc > 0, argv[0] != NULL */
12240 saveparam = shellparam;
12241 shellparam.malloced = 0;
12242 shellparam.nparam = argc;
12243 shellparam.p = argv;
12244 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012245
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012246 setinputfile(fullname, INPUT_PUSH_FILE);
12247 commandname = fullname;
12248 cmdloop(0);
12249 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012250
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012251 if (argc) {
12252 freeparam(&shellparam);
12253 shellparam = saveparam;
12254 };
12255
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012256 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012257}
12258
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012259static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012260exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012261{
12262 if (stoppedjobs())
12263 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012264 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012265 exitstatus = number(argv[1]);
12266 raise_exception(EXEXIT);
12267 /* NOTREACHED */
12268}
12269
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012270/*
12271 * Read a file containing shell functions.
12272 */
12273static void
12274readcmdfile(char *name)
12275{
12276 setinputfile(name, INPUT_PUSH_FILE);
12277 cmdloop(0);
12278 popfile();
12279}
12280
12281
Denis Vlasenkocc571512007-02-23 21:10:35 +000012282/* ============ find_command inplementation */
12283
12284/*
12285 * Resolve a command name. If you change this routine, you may have to
12286 * change the shellexec routine as well.
12287 */
12288static void
12289find_command(char *name, struct cmdentry *entry, int act, const char *path)
12290{
12291 struct tblentry *cmdp;
12292 int idx;
12293 int prev;
12294 char *fullname;
12295 struct stat statb;
12296 int e;
12297 int updatetbl;
12298 struct builtincmd *bcmd;
12299
12300 /* If name contains a slash, don't use PATH or hash table */
12301 if (strchr(name, '/') != NULL) {
12302 entry->u.index = -1;
12303 if (act & DO_ABS) {
12304 while (stat(name, &statb) < 0) {
12305#ifdef SYSV
12306 if (errno == EINTR)
12307 continue;
12308#endif
12309 entry->cmdtype = CMDUNKNOWN;
12310 return;
12311 }
12312 }
12313 entry->cmdtype = CMDNORMAL;
12314 return;
12315 }
12316
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012317/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012318
12319 updatetbl = (path == pathval());
12320 if (!updatetbl) {
12321 act |= DO_ALTPATH;
12322 if (strstr(path, "%builtin") != NULL)
12323 act |= DO_ALTBLTIN;
12324 }
12325
12326 /* If name is in the table, check answer will be ok */
12327 cmdp = cmdlookup(name, 0);
12328 if (cmdp != NULL) {
12329 int bit;
12330
12331 switch (cmdp->cmdtype) {
12332 default:
12333#if DEBUG
12334 abort();
12335#endif
12336 case CMDNORMAL:
12337 bit = DO_ALTPATH;
12338 break;
12339 case CMDFUNCTION:
12340 bit = DO_NOFUNC;
12341 break;
12342 case CMDBUILTIN:
12343 bit = DO_ALTBLTIN;
12344 break;
12345 }
12346 if (act & bit) {
12347 updatetbl = 0;
12348 cmdp = NULL;
12349 } else if (cmdp->rehash == 0)
12350 /* if not invalidated by cd, we're done */
12351 goto success;
12352 }
12353
12354 /* If %builtin not in path, check for builtin next */
12355 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012356 if (bcmd) {
12357 if (IS_BUILTIN_REGULAR(bcmd))
12358 goto builtin_success;
12359 if (act & DO_ALTPATH) {
12360 if (!(act & DO_ALTBLTIN))
12361 goto builtin_success;
12362 } else if (builtinloc <= 0) {
12363 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012364 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012365 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012366
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012367#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012368 {
12369 int applet_no = find_applet_by_name(name);
12370 if (applet_no >= 0) {
12371 entry->cmdtype = CMDNORMAL;
12372 entry->u.index = -2 - applet_no;
12373 return;
12374 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012375 }
12376#endif
12377
Denis Vlasenkocc571512007-02-23 21:10:35 +000012378 /* We have to search path. */
12379 prev = -1; /* where to start */
12380 if (cmdp && cmdp->rehash) { /* doing a rehash */
12381 if (cmdp->cmdtype == CMDBUILTIN)
12382 prev = builtinloc;
12383 else
12384 prev = cmdp->param.index;
12385 }
12386
12387 e = ENOENT;
12388 idx = -1;
12389 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012390 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012391 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012392 /* NB: code below will still use fullname
12393 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012394 idx++;
12395 if (pathopt) {
12396 if (prefix(pathopt, "builtin")) {
12397 if (bcmd)
12398 goto builtin_success;
12399 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012400 }
12401 if ((act & DO_NOFUNC)
12402 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012403 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012404 continue;
12405 }
12406 }
12407 /* if rehash, don't redo absolute path names */
12408 if (fullname[0] == '/' && idx <= prev) {
12409 if (idx < prev)
12410 continue;
12411 TRACE(("searchexec \"%s\": no change\n", name));
12412 goto success;
12413 }
12414 while (stat(fullname, &statb) < 0) {
12415#ifdef SYSV
12416 if (errno == EINTR)
12417 continue;
12418#endif
12419 if (errno != ENOENT && errno != ENOTDIR)
12420 e = errno;
12421 goto loop;
12422 }
12423 e = EACCES; /* if we fail, this will be the error */
12424 if (!S_ISREG(statb.st_mode))
12425 continue;
12426 if (pathopt) { /* this is a %func directory */
12427 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012428 /* NB: stalloc will return space pointed by fullname
12429 * (because we don't have any intervening allocations
12430 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012431 readcmdfile(fullname);
12432 cmdp = cmdlookup(name, 0);
12433 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12434 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12435 stunalloc(fullname);
12436 goto success;
12437 }
12438 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12439 if (!updatetbl) {
12440 entry->cmdtype = CMDNORMAL;
12441 entry->u.index = idx;
12442 return;
12443 }
12444 INT_OFF;
12445 cmdp = cmdlookup(name, 1);
12446 cmdp->cmdtype = CMDNORMAL;
12447 cmdp->param.index = idx;
12448 INT_ON;
12449 goto success;
12450 }
12451
12452 /* We failed. If there was an entry for this command, delete it */
12453 if (cmdp && updatetbl)
12454 delete_cmd_entry();
12455 if (act & DO_ERR)
12456 ash_msg("%s: %s", name, errmsg(e, "not found"));
12457 entry->cmdtype = CMDUNKNOWN;
12458 return;
12459
12460 builtin_success:
12461 if (!updatetbl) {
12462 entry->cmdtype = CMDBUILTIN;
12463 entry->u.cmd = bcmd;
12464 return;
12465 }
12466 INT_OFF;
12467 cmdp = cmdlookup(name, 1);
12468 cmdp->cmdtype = CMDBUILTIN;
12469 cmdp->param.cmd = bcmd;
12470 INT_ON;
12471 success:
12472 cmdp->rehash = 0;
12473 entry->cmdtype = cmdp->cmdtype;
12474 entry->u = cmdp->param;
12475}
12476
12477
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012478/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012479
Eric Andersencb57d552001-06-28 07:25:16 +000012480/*
Eric Andersencb57d552001-06-28 07:25:16 +000012481 * The trap builtin.
12482 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012483static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012484trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012485{
12486 char *action;
12487 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012488 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012489
Eric Andersenc470f442003-07-28 09:56:35 +000012490 nextopt(nullstr);
12491 ap = argptr;
12492 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012493 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012494 char *tr = trap_ptr[signo];
12495 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012496 /* note: bash adds "SIG", but only if invoked
12497 * as "bash". If called as "sh", or if set -o posix,
12498 * then it prints short signal names.
12499 * We are printing short names: */
12500 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012501 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012502 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012503 /* trap_ptr != trap only if we are in special-cased `trap` code.
12504 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012505 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012506 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012507 }
12508 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012509 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012510 if (trap_ptr != trap) {
12511 free(trap_ptr);
12512 trap_ptr = trap;
12513 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012514 */
Eric Andersencb57d552001-06-28 07:25:16 +000012515 return 0;
12516 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012517
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012518 action = NULL;
12519 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012520 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012521 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012522 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012523 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012524 if (signo < 0) {
12525 /* Mimic bash message exactly */
12526 ash_msg("%s: invalid signal specification", *ap);
12527 exitcode = 1;
12528 goto next;
12529 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012530 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012531 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012532 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012533 action = NULL;
12534 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012535 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012536 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012537 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012538 if (action)
12539 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012540 trap[signo] = action;
12541 if (signo != 0)
12542 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012543 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012544 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012545 ap++;
12546 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012547 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012548}
12549
Eric Andersenc470f442003-07-28 09:56:35 +000012550
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012551/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012552
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012553#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012554/*
12555 * Lists available builtins
12556 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012557static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012558helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012559{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012560 unsigned col;
12561 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012562
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012563 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012564 "Built-in commands:\n"
12565 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012566 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012567 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012568 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012569 if (col > 60) {
12570 out1fmt("\n");
12571 col = 0;
12572 }
12573 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012574#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012575 {
12576 const char *a = applet_names;
12577 while (*a) {
12578 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12579 if (col > 60) {
12580 out1fmt("\n");
12581 col = 0;
12582 }
12583 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012584 }
12585 }
12586#endif
12587 out1fmt("\n\n");
12588 return EXIT_SUCCESS;
12589}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012590#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012591
Eric Andersencb57d552001-06-28 07:25:16 +000012592/*
Eric Andersencb57d552001-06-28 07:25:16 +000012593 * The export and readonly commands.
12594 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012595static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012596exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012597{
12598 struct var *vp;
12599 char *name;
12600 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012601 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012602 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012603
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012604 if (nextopt("p") != 'p') {
12605 aptr = argptr;
12606 name = *aptr;
12607 if (name) {
12608 do {
12609 p = strchr(name, '=');
12610 if (p != NULL) {
12611 p++;
12612 } else {
12613 vp = *findvar(hashvar(name), name);
12614 if (vp) {
12615 vp->flags |= flag;
12616 continue;
12617 }
Eric Andersencb57d552001-06-28 07:25:16 +000012618 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012619 setvar(name, p, flag);
12620 } while ((name = *++aptr) != NULL);
12621 return 0;
12622 }
Eric Andersencb57d552001-06-28 07:25:16 +000012623 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012624 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012625 return 0;
12626}
12627
Eric Andersencb57d552001-06-28 07:25:16 +000012628/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012629 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012630 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012631static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012632unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012633{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012634 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012635
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012636 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012637 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012638 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012639}
12640
Eric Andersencb57d552001-06-28 07:25:16 +000012641/*
Eric Andersencb57d552001-06-28 07:25:16 +000012642 * The unset builtin command. We unset the function before we unset the
12643 * variable to allow a function to be unset when there is a readonly variable
12644 * with the same name.
12645 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012646static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012647unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012648{
12649 char **ap;
12650 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012651 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012652 int ret = 0;
12653
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012654 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012655 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012656 }
Eric Andersencb57d552001-06-28 07:25:16 +000012657
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012658 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012659 if (flag != 'f') {
12660 i = unsetvar(*ap);
12661 ret |= i;
12662 if (!(i & 2))
12663 continue;
12664 }
12665 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012666 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012667 }
Eric Andersenc470f442003-07-28 09:56:35 +000012668 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012669}
12670
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012671static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012672 ' ', offsetof(struct tms, tms_utime),
12673 '\n', offsetof(struct tms, tms_stime),
12674 ' ', offsetof(struct tms, tms_cutime),
12675 '\n', offsetof(struct tms, tms_cstime),
12676 0
12677};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012678static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012679timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012680{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012681 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012682 const unsigned char *p;
12683 struct tms buf;
12684
12685 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012686 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012687
12688 p = timescmd_str;
12689 do {
12690 t = *(clock_t *)(((char *) &buf) + p[1]);
12691 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012692 t = t % clk_tck;
12693 out1fmt("%lum%lu.%03lus%c",
12694 s / 60, s % 60,
12695 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012696 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012697 p += 2;
12698 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012699
Eric Andersencb57d552001-06-28 07:25:16 +000012700 return 0;
12701}
12702
Mike Frysinger98c52642009-04-02 10:02:37 +000012703#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012704/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012705 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012706 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012707 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012708 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012709 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012710static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012711letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012712{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012713 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012714
Denis Vlasenko68404f12008-03-17 09:00:54 +000012715 argv++;
12716 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012717 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012718 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012719 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012720 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012721
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012722 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012723}
Eric Andersenc470f442003-07-28 09:56:35 +000012724#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012725
Eric Andersenc470f442003-07-28 09:56:35 +000012726/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012727 * The read builtin. Options:
12728 * -r Do not interpret '\' specially
12729 * -s Turn off echo (tty only)
12730 * -n NCHARS Read NCHARS max
12731 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12732 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12733 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012734 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012735 * TODO: bash also has:
12736 * -a ARRAY Read into array[0],[1],etc
12737 * -d DELIM End on DELIM char, not newline
12738 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012739 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012740static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012741readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012742{
Denys Vlasenko73067272010-01-12 22:11:24 +010012743 char *opt_n = NULL;
12744 char *opt_p = NULL;
12745 char *opt_t = NULL;
12746 char *opt_u = NULL;
12747 int read_flags = 0;
12748 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012749 int i;
12750
Denys Vlasenko73067272010-01-12 22:11:24 +010012751 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012752 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012753 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012754 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012755 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012756 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012757 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012758 break;
12759 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012760 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012761 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012762 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012763 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012764 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012765 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012766 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012767 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012768 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012769 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012770 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012771 default:
12772 break;
12773 }
Eric Andersenc470f442003-07-28 09:56:35 +000012774 }
Paul Fox02eb9342005-09-07 16:56:02 +000012775
Denys Vlasenko03dad222010-01-12 23:29:57 +010012776 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012777 argptr,
12778 bltinlookup("IFS"), /* can be NULL */
12779 read_flags,
12780 opt_n,
12781 opt_p,
12782 opt_t,
12783 opt_u
12784 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012785
Denys Vlasenko73067272010-01-12 22:11:24 +010012786 if ((uintptr_t)r > 1)
12787 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012788
Denys Vlasenko73067272010-01-12 22:11:24 +010012789 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012790}
12791
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012792static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012793umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012794{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012795 static const char permuser[3] ALIGN1 = "ugo";
12796 static const char permmode[3] ALIGN1 = "rwx";
12797 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012798 S_IRUSR, S_IWUSR, S_IXUSR,
12799 S_IRGRP, S_IWGRP, S_IXGRP,
12800 S_IROTH, S_IWOTH, S_IXOTH
12801 };
12802
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012803 /* TODO: use bb_parse_mode() instead */
12804
Eric Andersenc470f442003-07-28 09:56:35 +000012805 char *ap;
12806 mode_t mask;
12807 int i;
12808 int symbolic_mode = 0;
12809
12810 while (nextopt("S") != '\0') {
12811 symbolic_mode = 1;
12812 }
12813
Denis Vlasenkob012b102007-02-19 22:43:01 +000012814 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012815 mask = umask(0);
12816 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012817 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012818
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012819 ap = *argptr;
12820 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012821 if (symbolic_mode) {
12822 char buf[18];
12823 char *p = buf;
12824
12825 for (i = 0; i < 3; i++) {
12826 int j;
12827
12828 *p++ = permuser[i];
12829 *p++ = '=';
12830 for (j = 0; j < 3; j++) {
12831 if ((mask & permmask[3 * i + j]) == 0) {
12832 *p++ = permmode[j];
12833 }
12834 }
12835 *p++ = ',';
12836 }
12837 *--p = 0;
12838 puts(buf);
12839 } else {
12840 out1fmt("%.4o\n", mask);
12841 }
12842 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012843 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012844 mask = 0;
12845 do {
12846 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012847 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012848 mask = (mask << 3) + (*ap - '0');
12849 } while (*++ap != '\0');
12850 umask(mask);
12851 } else {
12852 mask = ~mask & 0777;
12853 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012854 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012855 }
12856 umask(~mask & 0777);
12857 }
12858 }
12859 return 0;
12860}
12861
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012862static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012863ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012864{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012865 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012866}
12867
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012868/* ============ main() and helpers */
12869
12870/*
12871 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012872 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012873static void
12874exitshell(void)
12875{
12876 struct jmploc loc;
12877 char *p;
12878 int status;
12879
12880 status = exitstatus;
12881 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12882 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012883 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012884/* dash bug: it just does _exit(exitstatus) here
12885 * but we have to do setjobctl(0) first!
12886 * (bug is still not fixed in dash-0.5.3 - if you run dash
12887 * under Midnight Commander, on exit from dash MC is backgrounded) */
12888 status = exitstatus;
12889 goto out;
12890 }
12891 exception_handler = &loc;
12892 p = trap[0];
12893 if (p) {
12894 trap[0] = NULL;
12895 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012896 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012897 }
12898 flush_stdout_stderr();
12899 out:
12900 setjobctl(0);
12901 _exit(status);
12902 /* NOTREACHED */
12903}
12904
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012905static void
12906init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012907{
12908 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012909 /* we will never free this */
12910 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012911
12912 /* from trap.c: */
12913 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012914 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12915 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12916 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012917 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012918
12919 /* from var.c: */
12920 {
12921 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012922 const char *p;
12923 struct stat st1, st2;
12924
12925 initvar();
12926 for (envp = environ; envp && *envp; envp++) {
12927 if (strchr(*envp, '=')) {
12928 setvareq(*envp, VEXPORT|VTEXTFIXED);
12929 }
12930 }
12931
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012932 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012933
12934 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012935 if (p) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012936 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012937 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
12938 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012939 p = '\0';
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012940 }
12941 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012942 setpwd(p, 0);
12943 }
12944}
12945
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012946
12947//usage:#define ash_trivial_usage
12948//usage: "[-/+OPTCHARS] [-/+o OPTNAME]... [-c 'SCRIPT' [ARG0 [ARGS]] / SCRIPT_FILE [ARGS]]"
12949//usage:#define ash_full_usage "\n\n"
12950//usage: "Unix shell interpreter"
12951
12952//usage:#if ENABLE_FEATURE_SH_IS_ASH
12953//usage:# define sh_trivial_usage ash_trivial_usage
12954//usage:# define sh_full_usage ash_full_usage
12955//usage:#endif
12956//usage:#if ENABLE_FEATURE_BASH_IS_ASH
12957//usage:# define bash_trivial_usage ash_trivial_usage
12958//usage:# define bash_full_usage ash_full_usage
12959//usage:#endif
12960
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012961/*
12962 * Process the shell command line arguments.
12963 */
12964static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012965procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012966{
12967 int i;
12968 const char *xminusc;
12969 char **xargv;
12970
12971 xargv = argv;
12972 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012973 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012974 xargv++;
12975 for (i = 0; i < NOPTS; i++)
12976 optlist[i] = 2;
12977 argptr = xargv;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012978 if (options(/*cmdline:*/ 1)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012979 /* it already printed err message */
12980 raise_exception(EXERROR);
12981 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012982 xargv = argptr;
12983 xminusc = minusc;
12984 if (*xargv == NULL) {
12985 if (xminusc)
12986 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12987 sflag = 1;
12988 }
12989 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12990 iflag = 1;
12991 if (mflag == 2)
12992 mflag = iflag;
12993 for (i = 0; i < NOPTS; i++)
12994 if (optlist[i] == 2)
12995 optlist[i] = 0;
12996#if DEBUG == 2
12997 debug = 1;
12998#endif
12999 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13000 if (xminusc) {
13001 minusc = *xargv++;
13002 if (*xargv)
13003 goto setarg0;
13004 } else if (!sflag) {
13005 setinputfile(*xargv, 0);
13006 setarg0:
13007 arg0 = *xargv++;
13008 commandname = arg0;
13009 }
13010
13011 shellparam.p = xargv;
13012#if ENABLE_ASH_GETOPTS
13013 shellparam.optind = 1;
13014 shellparam.optoff = -1;
13015#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013016 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013017 while (*xargv) {
13018 shellparam.nparam++;
13019 xargv++;
13020 }
13021 optschanged();
13022}
13023
13024/*
13025 * Read /etc/profile or .profile.
13026 */
13027static void
13028read_profile(const char *name)
13029{
13030 int skip;
13031
13032 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13033 return;
13034 skip = cmdloop(0);
13035 popfile();
13036 if (skip)
13037 exitshell();
13038}
13039
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013040/*
13041 * This routine is called when an error or an interrupt occurs in an
13042 * interactive shell and control is returned to the main command loop.
13043 */
13044static void
13045reset(void)
13046{
13047 /* from eval.c: */
13048 evalskip = 0;
13049 loopnest = 0;
13050 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013051 g_parsefile->left_in_buffer = 0;
13052 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013053 popallfiles();
13054 /* from parser.c: */
13055 tokpushback = 0;
13056 checkkwd = 0;
13057 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013058 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013059}
13060
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013061#if PROFILE
13062static short profile_buf[16384];
13063extern int etext();
13064#endif
13065
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013066/*
13067 * Main routine. We initialize things, parse the arguments, execute
13068 * profiles if we're a login shell, and then call cmdloop to execute
13069 * commands. The setjmp call sets up the location to jump to when an
13070 * exception occurs. When an exception occurs the variable "state"
13071 * is used to figure out how far we had gotten.
13072 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013073int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013074int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013075{
Mike Frysinger98c52642009-04-02 10:02:37 +000013076 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013077 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013078 struct jmploc jmploc;
13079 struct stackmark smark;
13080
Denis Vlasenko01631112007-12-16 17:20:38 +000013081 /* Initialize global data */
13082 INIT_G_misc();
13083 INIT_G_memstack();
13084 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013085#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013086 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013087#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013088 INIT_G_cmdtable();
13089
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013090#if PROFILE
13091 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13092#endif
13093
13094#if ENABLE_FEATURE_EDITING
13095 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13096#endif
13097 state = 0;
13098 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013099 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013100 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013101
13102 reset();
13103
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013104 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013105 if (e == EXERROR)
13106 exitstatus = 2;
13107 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013108 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013109 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013110 }
13111 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013112 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013113 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013114
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013115 popstackmark(&smark);
13116 FORCE_INT_ON; /* enable interrupts */
13117 if (s == 1)
13118 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013119 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013120 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013121 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013122 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013123 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013124 }
13125 exception_handler = &jmploc;
13126#if DEBUG
13127 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013128 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013129 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013130#endif
13131 rootpid = getpid();
13132
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013133 init();
13134 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013135 procargs(argv);
13136
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013137#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13138 if (iflag) {
13139 const char *hp = lookupvar("HISTFILE");
13140
13141 if (hp == NULL) {
13142 hp = lookupvar("HOME");
13143 if (hp != NULL) {
13144 char *defhp = concat_path_file(hp, ".ash_history");
13145 setvar("HISTFILE", defhp, 0);
13146 free(defhp);
13147 }
13148 }
13149 }
13150#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013151 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013152 isloginsh = 1;
13153 if (isloginsh) {
13154 state = 1;
13155 read_profile("/etc/profile");
13156 state1:
13157 state = 2;
13158 read_profile(".profile");
13159 }
13160 state2:
13161 state = 3;
13162 if (
13163#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013164 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013165#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013166 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013167 ) {
13168 shinit = lookupvar("ENV");
13169 if (shinit != NULL && *shinit != '\0') {
13170 read_profile(shinit);
13171 }
13172 }
13173 state3:
13174 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013175 if (minusc) {
13176 /* evalstring pushes parsefile stack.
13177 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013178 * is one of stacked source fds.
13179 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013180 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013181 // ^^ not necessary since now we special-case fd 0
13182 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013183 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013184 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013185
13186 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013187#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013188 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013189 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013190 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013191 line_input_state->hist_file = hp;
13192 }
13193#endif
13194 state4: /* XXX ??? - why isn't this before the "if" statement */
13195 cmdloop(1);
13196 }
13197#if PROFILE
13198 monitor(0);
13199#endif
13200#ifdef GPROF
13201 {
13202 extern void _mcleanup(void);
13203 _mcleanup();
13204 }
13205#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013206 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013207 exitshell();
13208 /* NOTREACHED */
13209}
13210
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013211
Eric Andersendf82f612001-06-28 07:46:40 +000013212/*-
13213 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013214 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013215 *
13216 * This code is derived from software contributed to Berkeley by
13217 * Kenneth Almquist.
13218 *
13219 * Redistribution and use in source and binary forms, with or without
13220 * modification, are permitted provided that the following conditions
13221 * are met:
13222 * 1. Redistributions of source code must retain the above copyright
13223 * notice, this list of conditions and the following disclaimer.
13224 * 2. Redistributions in binary form must reproduce the above copyright
13225 * notice, this list of conditions and the following disclaimer in the
13226 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013227 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013228 * may be used to endorse or promote products derived from this software
13229 * without specific prior written permission.
13230 *
13231 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13232 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13233 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13234 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13235 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13236 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13237 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13238 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13239 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13240 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13241 * SUCH DAMAGE.
13242 */