blob: 0bcbf9028d2d2dee1b38889b4e408b3b70a4324e [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000039#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000040#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020043#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010044
45#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010046#if ENABLE_SH_MATH_SUPPORT
47# include "math.h"
48#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020049#if ENABLE_ASH_RANDOM_SUPPORT
50# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020051#else
52# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020053#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000054
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020055#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010056#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000057/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020058# undef CONFIG_FEATURE_SH_STANDALONE
59# undef ENABLE_FEATURE_SH_STANDALONE
60# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010061# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020062# define ENABLE_FEATURE_SH_STANDALONE 0
63# define IF_FEATURE_SH_STANDALONE(...)
64# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000065#endif
66
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000067#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000068# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000069#endif
70
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010071#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000072# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000073#endif
74
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +010075//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
76//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
77//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
Denys Vlasenko771f1992010-07-16 14:31:34 +020078
Denys Vlasenkod383b492010-09-06 10:22:13 +020079//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko771f1992010-07-16 14:31:34 +020080//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
81
82//config:config ASH
83//config: bool "ash"
84//config: default y
85//config: depends on !NOMMU
86//config: help
87//config: Tha 'ash' shell adds about 60k in the default configuration and is
88//config: the most complete and most pedantically correct shell included with
89//config: busybox. This shell is actually a derivative of the Debian 'dash'
90//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
91//config: (written by Kenneth Almquist) from NetBSD.
92//config:
93//config:config ASH_BASH_COMPAT
94//config: bool "bash-compatible extensions"
95//config: default y
96//config: depends on ASH
97//config: help
98//config: Enable bash-compatible extensions.
99//config:
100//config:config ASH_JOB_CONTROL
101//config: bool "Job control"
102//config: default y
103//config: depends on ASH
104//config: help
105//config: Enable job control in the ash shell.
106//config:
107//config:config ASH_ALIAS
108//config: bool "alias support"
109//config: default y
110//config: depends on ASH
111//config: help
112//config: Enable alias support in the ash shell.
113//config:
114//config:config ASH_GETOPTS
115//config: bool "Builtin getopt to parse positional parameters"
116//config: default y
117//config: depends on ASH
118//config: help
119//config: Enable getopts builtin in the ash shell.
120//config:
121//config:config ASH_BUILTIN_ECHO
122//config: bool "Builtin version of 'echo'"
123//config: default y
124//config: depends on ASH
125//config: help
126//config: Enable support for echo, builtin to ash.
127//config:
128//config:config ASH_BUILTIN_PRINTF
129//config: bool "Builtin version of 'printf'"
130//config: default y
131//config: depends on ASH
132//config: help
133//config: Enable support for printf, builtin to ash.
134//config:
135//config:config ASH_BUILTIN_TEST
136//config: bool "Builtin version of 'test'"
137//config: default y
138//config: depends on ASH
139//config: help
140//config: Enable support for test, builtin to ash.
141//config:
142//config:config ASH_CMDCMD
143//config: bool "'command' command to override shell builtins"
144//config: default y
145//config: depends on ASH
146//config: help
147//config: Enable support for the ash 'command' builtin, which allows
148//config: you to run the specified command with the specified arguments,
149//config: even when there is an ash builtin command with the same name.
150//config:
151//config:config ASH_MAIL
152//config: bool "Check for new mail on interactive shells"
153//config: default n
154//config: depends on ASH
155//config: help
156//config: Enable "check for new mail" in the ash shell.
157//config:
158//config:config ASH_OPTIMIZE_FOR_SIZE
159//config: bool "Optimize for size instead of speed"
160//config: default y
161//config: depends on ASH
162//config: help
163//config: Compile ash for reduced size at the price of speed.
164//config:
165//config:config ASH_RANDOM_SUPPORT
166//config: bool "Pseudorandom generator and $RANDOM variable"
167//config: default y
168//config: depends on ASH
169//config: help
170//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
171//config: Each read of "$RANDOM" will generate a new pseudorandom value.
172//config: You can reset the generator by using a specified start value.
173//config: After "unset RANDOM" the generator will switch off and this
174//config: variable will no longer have special treatment.
175//config:
176//config:config ASH_EXPAND_PRMT
177//config: bool "Expand prompt string"
178//config: default y
179//config: depends on ASH
180//config: help
181//config: "PS#" may contain volatile content, such as backquote commands.
182//config: This option recreates the prompt string from the environment
183//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200184//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200185
186//usage:#define ash_trivial_usage NOUSAGE_STR
187//usage:#define ash_full_usage ""
188//usage:#define sh_trivial_usage NOUSAGE_STR
189//usage:#define sh_full_usage ""
190//usage:#define bash_trivial_usage NOUSAGE_STR
191//usage:#define bash_full_usage ""
192
Denis Vlasenkob012b102007-02-19 22:43:01 +0000193
Denis Vlasenko01631112007-12-16 17:20:38 +0000194/* ============ Hash table sizes. Configurable. */
195
196#define VTABSIZE 39
197#define ATABSIZE 39
198#define CMDTABLESIZE 31 /* should be prime */
199
200
Denis Vlasenkob012b102007-02-19 22:43:01 +0000201/* ============ Shell options */
202
203static const char *const optletters_optnames[] = {
204 "e" "errexit",
205 "f" "noglob",
206 "I" "ignoreeof",
207 "i" "interactive",
208 "m" "monitor",
209 "n" "noexec",
210 "s" "stdin",
211 "x" "xtrace",
212 "v" "verbose",
213 "C" "noclobber",
214 "a" "allexport",
215 "b" "notify",
216 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100217 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100218#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100219 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100220#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000221#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000222 ,"\0" "nolog"
223 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000224#endif
225};
226
Denys Vlasenko285ad152009-12-04 23:02:27 +0100227#define optletters(n) optletters_optnames[n][0]
228#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000229
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000230enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231
Eric Andersenc470f442003-07-28 09:56:35 +0000232
Denis Vlasenkob012b102007-02-19 22:43:01 +0000233/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000234
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200235#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000236
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000237/*
Eric Andersenc470f442003-07-28 09:56:35 +0000238 * We enclose jmp_buf in a structure so that we can declare pointers to
239 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000240 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000241 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000242 * exception handlers, the user should save the value of handler on entry
243 * to an inner scope, set handler to point to a jmploc structure for the
244 * inner scope, and restore handler on exit from the scope.
245 */
Eric Andersenc470f442003-07-28 09:56:35 +0000246struct jmploc {
247 jmp_buf loc;
248};
Denis Vlasenko01631112007-12-16 17:20:38 +0000249
250struct globals_misc {
251 /* pid of main shell */
252 int rootpid;
253 /* shell level: 0 for the main shell, 1 for its children, and so on */
254 int shlvl;
255#define rootshell (!shlvl)
256 char *minusc; /* argument to -c option */
257
258 char *curdir; // = nullstr; /* current working directory */
259 char *physdir; // = nullstr; /* physical working directory */
260
261 char *arg0; /* value of $0 */
262
263 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000264
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200265 volatile int suppress_int; /* counter */
266 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000267 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200268 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000269 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000270 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000271#define EXINT 0 /* SIGINT received */
272#define EXERROR 1 /* a generic error */
273#define EXSHELLPROC 2 /* execute a shell procedure */
274#define EXEXEC 3 /* command execution failed */
275#define EXEXIT 4 /* exit the shell */
276#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000277
Denis Vlasenko01631112007-12-16 17:20:38 +0000278 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000279 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000280
281 char optlist[NOPTS];
282#define eflag optlist[0]
283#define fflag optlist[1]
284#define Iflag optlist[2]
285#define iflag optlist[3]
286#define mflag optlist[4]
287#define nflag optlist[5]
288#define sflag optlist[6]
289#define xflag optlist[7]
290#define vflag optlist[8]
291#define Cflag optlist[9]
292#define aflag optlist[10]
293#define bflag optlist[11]
294#define uflag optlist[12]
295#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100296#if ENABLE_ASH_BASH_COMPAT
297# define pipefail optlist[14]
298#else
299# define pipefail 0
300#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000301#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100302# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
303# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000304#endif
305
306 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000307 /*
308 * Sigmode records the current value of the signal handlers for the various
309 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000310 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000311 */
312 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000313#define S_DFL 1 /* default signal handling (SIG_DFL) */
314#define S_CATCH 2 /* signal is caught */
315#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000316#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000317
Denis Vlasenko01631112007-12-16 17:20:38 +0000318 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000319 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200320 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000321 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200322 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000323
324 /* Rarely referenced stuff */
325#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200326 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000327#endif
328 pid_t backgndpid; /* pid of last background process */
329 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000330};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000331extern struct globals_misc *const ash_ptr_to_globals_misc;
332#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000333#define rootpid (G_misc.rootpid )
334#define shlvl (G_misc.shlvl )
335#define minusc (G_misc.minusc )
336#define curdir (G_misc.curdir )
337#define physdir (G_misc.physdir )
338#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000339#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000340#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200341#define suppress_int (G_misc.suppress_int )
342#define pending_int (G_misc.pending_int )
343#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000344#define isloginsh (G_misc.isloginsh )
345#define nullstr (G_misc.nullstr )
346#define optlist (G_misc.optlist )
347#define sigmode (G_misc.sigmode )
348#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200349#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000350#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200351#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200352#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000353#define backgndpid (G_misc.backgndpid )
354#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000355#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000356 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
357 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000358 curdir = nullstr; \
359 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200360 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000361} while (0)
362
363
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000364/* ============ DEBUG */
365#if DEBUG
366static void trace_printf(const char *fmt, ...);
367static void trace_vprintf(const char *fmt, va_list va);
368# define TRACE(param) trace_printf param
369# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000370# define close(fd) do { \
371 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000372 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200373 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000374 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000375} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000376#else
377# define TRACE(param)
378# define TRACEV(param)
379#endif
380
381
Denis Vlasenko559691a2008-10-05 18:39:31 +0000382/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000383#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
384
Denis Vlasenko559691a2008-10-05 18:39:31 +0000385static int isdigit_str9(const char *str)
386{
387 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
388 while (--maxlen && isdigit(*str))
389 str++;
390 return (*str == '\0');
391}
Denis Vlasenko01631112007-12-16 17:20:38 +0000392
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200393static const char *var_end(const char *var)
394{
395 while (*var)
396 if (*var++ == '=')
397 break;
398 return var;
399}
400
Denis Vlasenko559691a2008-10-05 18:39:31 +0000401
402/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000403/*
Eric Andersen2870d962001-07-02 17:27:21 +0000404 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000405 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000406 * much more efficient and portable. (But hacking the kernel is so much
407 * more fun than worrying about efficiency and portability. :-))
408 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000409#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200410 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000411 xbarrier(); \
412} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000413
414/*
415 * Called to raise an exception. Since C doesn't include exceptions, we
416 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000417 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000418 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000419static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000420static void
421raise_exception(int e)
422{
423#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000424 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000425 abort();
426#endif
427 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000428 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000429 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000430}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000431#if DEBUG
432#define raise_exception(e) do { \
433 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
434 raise_exception(e); \
435} while (0)
436#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000437
438/*
439 * Called from trap.c when a SIGINT is received. (If the user specifies
440 * that SIGINT is to be trapped or ignored using the trap builtin, then
441 * this routine is not called.) Suppressint is nonzero when interrupts
442 * are held using the INT_OFF macro. (The test for iflag is just
443 * defensive programming.)
444 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000445static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000446static void
447raise_interrupt(void)
448{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000449 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000450
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200451 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000452 /* Signal is not automatically unmasked after it is raised,
453 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000454 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200455 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000456
Denis Vlasenko4b875702009-03-19 13:30:04 +0000457 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000458 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
459 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000460 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000461 signal(SIGINT, SIG_DFL);
462 raise(SIGINT);
463 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000464 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000465 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000466 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000467 /* NOTREACHED */
468}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000469#if DEBUG
470#define raise_interrupt() do { \
471 TRACE(("raising interrupt on line %d\n", __LINE__)); \
472 raise_interrupt(); \
473} while (0)
474#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000475
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000476static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000477int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000478{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000479 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200480 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000481 raise_interrupt();
482 }
483}
484#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000485static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000486force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000487{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000488 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200489 suppress_int = 0;
490 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000491 raise_interrupt();
492}
493#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000494
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200495#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000496
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000497#define RESTORE_INT(v) do { \
498 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200499 suppress_int = (v); \
500 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000501 raise_interrupt(); \
502} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000503
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000504
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000505/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000506
Eric Andersenc470f442003-07-28 09:56:35 +0000507static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000508outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000509{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000510 INT_OFF;
511 fputs(p, file);
512 INT_ON;
513}
514
515static void
516flush_stdout_stderr(void)
517{
518 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100519 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000520 INT_ON;
521}
522
523static void
524outcslow(int c, FILE *dest)
525{
526 INT_OFF;
527 putc(c, dest);
528 fflush(dest);
529 INT_ON;
530}
531
532static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
533static int
534out1fmt(const char *fmt, ...)
535{
536 va_list ap;
537 int r;
538
539 INT_OFF;
540 va_start(ap, fmt);
541 r = vprintf(fmt, ap);
542 va_end(ap);
543 INT_ON;
544 return r;
545}
546
547static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
548static int
549fmtstr(char *outbuf, size_t length, const char *fmt, ...)
550{
551 va_list ap;
552 int ret;
553
554 va_start(ap, fmt);
555 INT_OFF;
556 ret = vsnprintf(outbuf, length, fmt, ap);
557 va_end(ap);
558 INT_ON;
559 return ret;
560}
561
562static void
563out1str(const char *p)
564{
565 outstr(p, stdout);
566}
567
568static void
569out2str(const char *p)
570{
571 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100572 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000573}
574
575
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000576/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000577
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000578/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100579#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200580#define CTLESC ((unsigned char)'\201') /* escape next character */
581#define CTLVAR ((unsigned char)'\202') /* variable defn */
582#define CTLENDVAR ((unsigned char)'\203')
583#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000584#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
585/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200586#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
587#define CTLENDARI ((unsigned char)'\207')
588#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100589#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000590
591/* variable substitution byte (follows CTLVAR) */
592#define VSTYPE 0x0f /* type of variable substitution */
593#define VSNUL 0x10 /* colon--treat the empty string as unset */
594#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
595
596/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000597#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
598#define VSMINUS 0x2 /* ${var-text} */
599#define VSPLUS 0x3 /* ${var+text} */
600#define VSQUESTION 0x4 /* ${var?message} */
601#define VSASSIGN 0x5 /* ${var=text} */
602#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
603#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
604#define VSTRIMLEFT 0x8 /* ${var#pattern} */
605#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
606#define VSLENGTH 0xa /* ${#var} */
607#if ENABLE_ASH_BASH_COMPAT
608#define VSSUBSTR 0xc /* ${var:position:length} */
609#define VSREPLACE 0xd /* ${var/pattern/replacement} */
610#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
611#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000612
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000613static const char dolatstr[] ALIGN1 = {
614 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
615};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000616
Denis Vlasenko559691a2008-10-05 18:39:31 +0000617#define NCMD 0
618#define NPIPE 1
619#define NREDIR 2
620#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000622#define NAND 5
623#define NOR 6
624#define NSEMI 7
625#define NIF 8
626#define NWHILE 9
627#define NUNTIL 10
628#define NFOR 11
629#define NCASE 12
630#define NCLIST 13
631#define NDEFUN 14
632#define NARG 15
633#define NTO 16
634#if ENABLE_ASH_BASH_COMPAT
635#define NTO2 17
636#endif
637#define NCLOBBER 18
638#define NFROM 19
639#define NFROMTO 20
640#define NAPPEND 21
641#define NTOFD 22
642#define NFROMFD 23
643#define NHERE 24
644#define NXHERE 25
645#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000646#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000647
648union node;
649
650struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000651 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000652 union node *assign;
653 union node *args;
654 union node *redirect;
655};
656
657struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000658 smallint type;
659 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000660 struct nodelist *cmdlist;
661};
662
663struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000664 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000665 union node *n;
666 union node *redirect;
667};
668
669struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000670 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000671 union node *ch1;
672 union node *ch2;
673};
674
675struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000676 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000677 union node *test;
678 union node *ifpart;
679 union node *elsepart;
680};
681
682struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000683 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000684 union node *args;
685 union node *body;
686 char *var;
687};
688
689struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000690 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000691 union node *expr;
692 union node *cases;
693};
694
695struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000696 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000697 union node *next;
698 union node *pattern;
699 union node *body;
700};
701
702struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000703 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000704 union node *next;
705 char *text;
706 struct nodelist *backquote;
707};
708
Denis Vlasenko559691a2008-10-05 18:39:31 +0000709/* nfile and ndup layout must match!
710 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
711 * that it is actually NTO2 (>&file), and change its type.
712 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000713struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000714 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000715 union node *next;
716 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000717 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000718 union node *fname;
719 char *expfname;
720};
721
722struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000723 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000724 union node *next;
725 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000726 int dupfd;
727 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000728 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000729};
730
731struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000732 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000733 union node *next;
734 int fd;
735 union node *doc;
736};
737
738struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000739 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000740 union node *com;
741};
742
743union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000744 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000745 struct ncmd ncmd;
746 struct npipe npipe;
747 struct nredir nredir;
748 struct nbinary nbinary;
749 struct nif nif;
750 struct nfor nfor;
751 struct ncase ncase;
752 struct nclist nclist;
753 struct narg narg;
754 struct nfile nfile;
755 struct ndup ndup;
756 struct nhere nhere;
757 struct nnot nnot;
758};
759
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200760/*
761 * NODE_EOF is returned by parsecmd when it encounters an end of file.
762 * It must be distinct from NULL.
763 */
764#define NODE_EOF ((union node *) -1L)
765
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000766struct nodelist {
767 struct nodelist *next;
768 union node *n;
769};
770
771struct funcnode {
772 int count;
773 union node n;
774};
775
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000776/*
777 * Free a parse tree.
778 */
779static void
780freefunc(struct funcnode *f)
781{
782 if (f && --f->count < 0)
783 free(f);
784}
785
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000786
787/* ============ Debugging output */
788
789#if DEBUG
790
791static FILE *tracefile;
792
793static void
794trace_printf(const char *fmt, ...)
795{
796 va_list va;
797
798 if (debug != 1)
799 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000800 if (DEBUG_TIME)
801 fprintf(tracefile, "%u ", (int) time(NULL));
802 if (DEBUG_PID)
803 fprintf(tracefile, "[%u] ", (int) getpid());
804 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200805 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000806 va_start(va, fmt);
807 vfprintf(tracefile, fmt, va);
808 va_end(va);
809}
810
811static void
812trace_vprintf(const char *fmt, va_list va)
813{
814 if (debug != 1)
815 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000816 if (DEBUG_TIME)
817 fprintf(tracefile, "%u ", (int) time(NULL));
818 if (DEBUG_PID)
819 fprintf(tracefile, "[%u] ", (int) getpid());
820 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200821 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000822 vfprintf(tracefile, fmt, va);
823}
824
825static void
826trace_puts(const char *s)
827{
828 if (debug != 1)
829 return;
830 fputs(s, tracefile);
831}
832
833static void
834trace_puts_quoted(char *s)
835{
836 char *p;
837 char c;
838
839 if (debug != 1)
840 return;
841 putc('"', tracefile);
842 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100843 switch ((unsigned char)*p) {
844 case '\n': c = 'n'; goto backslash;
845 case '\t': c = 't'; goto backslash;
846 case '\r': c = 'r'; goto backslash;
847 case '\"': c = '\"'; goto backslash;
848 case '\\': c = '\\'; goto backslash;
849 case CTLESC: c = 'e'; goto backslash;
850 case CTLVAR: c = 'v'; goto backslash;
851 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
852 case CTLBACKQ: c = 'q'; goto backslash;
853 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000854 backslash:
855 putc('\\', tracefile);
856 putc(c, tracefile);
857 break;
858 default:
859 if (*p >= ' ' && *p <= '~')
860 putc(*p, tracefile);
861 else {
862 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100863 putc((*p >> 6) & 03, tracefile);
864 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000865 putc(*p & 07, tracefile);
866 }
867 break;
868 }
869 }
870 putc('"', tracefile);
871}
872
873static void
874trace_puts_args(char **ap)
875{
876 if (debug != 1)
877 return;
878 if (!*ap)
879 return;
880 while (1) {
881 trace_puts_quoted(*ap);
882 if (!*++ap) {
883 putc('\n', tracefile);
884 break;
885 }
886 putc(' ', tracefile);
887 }
888}
889
890static void
891opentrace(void)
892{
893 char s[100];
894#ifdef O_APPEND
895 int flags;
896#endif
897
898 if (debug != 1) {
899 if (tracefile)
900 fflush(tracefile);
901 /* leave open because libedit might be using it */
902 return;
903 }
904 strcpy(s, "./trace");
905 if (tracefile) {
906 if (!freopen(s, "a", tracefile)) {
907 fprintf(stderr, "Can't re-open %s\n", s);
908 debug = 0;
909 return;
910 }
911 } else {
912 tracefile = fopen(s, "a");
913 if (tracefile == NULL) {
914 fprintf(stderr, "Can't open %s\n", s);
915 debug = 0;
916 return;
917 }
918 }
919#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000920 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000921 if (flags >= 0)
922 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
923#endif
924 setlinebuf(tracefile);
925 fputs("\nTracing started.\n", tracefile);
926}
927
928static void
929indent(int amount, char *pfx, FILE *fp)
930{
931 int i;
932
933 for (i = 0; i < amount; i++) {
934 if (pfx && i == amount - 1)
935 fputs(pfx, fp);
936 putc('\t', fp);
937 }
938}
939
940/* little circular references here... */
941static void shtree(union node *n, int ind, char *pfx, FILE *fp);
942
943static void
944sharg(union node *arg, FILE *fp)
945{
946 char *p;
947 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100948 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000949
950 if (arg->type != NARG) {
951 out1fmt("<node type %d>\n", arg->type);
952 abort();
953 }
954 bqlist = arg->narg.backquote;
955 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100956 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000957 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700958 p++;
959 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000960 break;
961 case CTLVAR:
962 putc('$', fp);
963 putc('{', fp);
964 subtype = *++p;
965 if (subtype == VSLENGTH)
966 putc('#', fp);
967
Dan Fandrich77d48722010-09-07 23:38:28 -0700968 while (*p != '=') {
969 putc(*p, fp);
970 p++;
971 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000972
973 if (subtype & VSNUL)
974 putc(':', fp);
975
976 switch (subtype & VSTYPE) {
977 case VSNORMAL:
978 putc('}', fp);
979 break;
980 case VSMINUS:
981 putc('-', fp);
982 break;
983 case VSPLUS:
984 putc('+', fp);
985 break;
986 case VSQUESTION:
987 putc('?', fp);
988 break;
989 case VSASSIGN:
990 putc('=', fp);
991 break;
992 case VSTRIMLEFT:
993 putc('#', fp);
994 break;
995 case VSTRIMLEFTMAX:
996 putc('#', fp);
997 putc('#', fp);
998 break;
999 case VSTRIMRIGHT:
1000 putc('%', fp);
1001 break;
1002 case VSTRIMRIGHTMAX:
1003 putc('%', fp);
1004 putc('%', fp);
1005 break;
1006 case VSLENGTH:
1007 break;
1008 default:
1009 out1fmt("<subtype %d>", subtype);
1010 }
1011 break;
1012 case CTLENDVAR:
1013 putc('}', fp);
1014 break;
1015 case CTLBACKQ:
1016 case CTLBACKQ|CTLQUOTE:
1017 putc('$', fp);
1018 putc('(', fp);
1019 shtree(bqlist->n, -1, NULL, fp);
1020 putc(')', fp);
1021 break;
1022 default:
1023 putc(*p, fp);
1024 break;
1025 }
1026 }
1027}
1028
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001029static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001030shcmd(union node *cmd, FILE *fp)
1031{
1032 union node *np;
1033 int first;
1034 const char *s;
1035 int dftfd;
1036
1037 first = 1;
1038 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001039 if (!first)
1040 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001041 sharg(np, fp);
1042 first = 0;
1043 }
1044 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001045 if (!first)
1046 putc(' ', fp);
1047 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001048 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001049 case NTO: s = ">>"+1; dftfd = 1; break;
1050 case NCLOBBER: s = ">|"; dftfd = 1; break;
1051 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001052#if ENABLE_ASH_BASH_COMPAT
1053 case NTO2:
1054#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001055 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001056 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001057 case NFROMFD: s = "<&"; break;
1058 case NFROMTO: s = "<>"; break;
1059 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001060 }
1061 if (np->nfile.fd != dftfd)
1062 fprintf(fp, "%d", np->nfile.fd);
1063 fputs(s, fp);
1064 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1065 fprintf(fp, "%d", np->ndup.dupfd);
1066 } else {
1067 sharg(np->nfile.fname, fp);
1068 }
1069 first = 0;
1070 }
1071}
1072
1073static void
1074shtree(union node *n, int ind, char *pfx, FILE *fp)
1075{
1076 struct nodelist *lp;
1077 const char *s;
1078
1079 if (n == NULL)
1080 return;
1081
1082 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001083
1084 if (n == NODE_EOF) {
1085 fputs("<EOF>", fp);
1086 return;
1087 }
1088
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001089 switch (n->type) {
1090 case NSEMI:
1091 s = "; ";
1092 goto binop;
1093 case NAND:
1094 s = " && ";
1095 goto binop;
1096 case NOR:
1097 s = " || ";
1098 binop:
1099 shtree(n->nbinary.ch1, ind, NULL, fp);
1100 /* if (ind < 0) */
1101 fputs(s, fp);
1102 shtree(n->nbinary.ch2, ind, NULL, fp);
1103 break;
1104 case NCMD:
1105 shcmd(n, fp);
1106 if (ind >= 0)
1107 putc('\n', fp);
1108 break;
1109 case NPIPE:
1110 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001111 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001112 if (lp->next)
1113 fputs(" | ", fp);
1114 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001115 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001116 fputs(" &", fp);
1117 if (ind >= 0)
1118 putc('\n', fp);
1119 break;
1120 default:
1121 fprintf(fp, "<node type %d>", n->type);
1122 if (ind >= 0)
1123 putc('\n', fp);
1124 break;
1125 }
1126}
1127
1128static void
1129showtree(union node *n)
1130{
1131 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001132 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001133}
1134
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001135#endif /* DEBUG */
1136
1137
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001138/* ============ Parser data */
1139
1140/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001141 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1142 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001143struct strlist {
1144 struct strlist *next;
1145 char *text;
1146};
1147
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001148struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001149
Denis Vlasenkob012b102007-02-19 22:43:01 +00001150struct strpush {
1151 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001152 char *prev_string;
1153 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001154#if ENABLE_ASH_ALIAS
1155 struct alias *ap; /* if push was associated with an alias */
1156#endif
1157 char *string; /* remember the string since it may change */
1158};
1159
1160struct parsefile {
1161 struct parsefile *prev; /* preceding file on stack */
1162 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001163 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001164 int left_in_line; /* number of chars left in this line */
1165 int left_in_buffer; /* number of chars left in this buffer past the line */
1166 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001167 char *buf; /* input buffer */
1168 struct strpush *strpush; /* for pushing strings at this level */
1169 struct strpush basestrpush; /* so pushing one is fast */
1170};
1171
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001172static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001173static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001174static int startlinno; /* line # where last token started */
1175static char *commandname; /* currently executing command */
1176static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001177static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001178
1179
1180/* ============ Message printing */
1181
1182static void
1183ash_vmsg(const char *msg, va_list ap)
1184{
1185 fprintf(stderr, "%s: ", arg0);
1186 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001187 if (strcmp(arg0, commandname))
1188 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001189 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001190 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001191 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001192 vfprintf(stderr, msg, ap);
1193 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001194}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001195
1196/*
1197 * Exverror is called to raise the error exception. If the second argument
1198 * is not NULL then error prints an error message using printf style
1199 * formatting. It then raises the error exception.
1200 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001201static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001202static void
1203ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001204{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001205#if DEBUG
1206 if (msg) {
1207 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1208 TRACEV((msg, ap));
1209 TRACE(("\") pid=%d\n", getpid()));
1210 } else
1211 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1212 if (msg)
1213#endif
1214 ash_vmsg(msg, ap);
1215
1216 flush_stdout_stderr();
1217 raise_exception(cond);
1218 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001219}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001220
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001221static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001222static void
1223ash_msg_and_raise_error(const char *msg, ...)
1224{
1225 va_list ap;
1226
1227 va_start(ap, msg);
1228 ash_vmsg_and_raise(EXERROR, msg, ap);
1229 /* NOTREACHED */
1230 va_end(ap);
1231}
1232
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001233static void raise_error_syntax(const char *) NORETURN;
1234static void
1235raise_error_syntax(const char *msg)
1236{
1237 ash_msg_and_raise_error("syntax error: %s", msg);
1238 /* NOTREACHED */
1239}
1240
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001241static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001242static void
1243ash_msg_and_raise(int cond, const char *msg, ...)
1244{
1245 va_list ap;
1246
1247 va_start(ap, msg);
1248 ash_vmsg_and_raise(cond, msg, ap);
1249 /* NOTREACHED */
1250 va_end(ap);
1251}
1252
1253/*
1254 * error/warning routines for external builtins
1255 */
1256static void
1257ash_msg(const char *fmt, ...)
1258{
1259 va_list ap;
1260
1261 va_start(ap, fmt);
1262 ash_vmsg(fmt, ap);
1263 va_end(ap);
1264}
1265
1266/*
1267 * Return a string describing an error. The returned string may be a
1268 * pointer to a static buffer that will be overwritten on the next call.
1269 * Action describes the operation that got the error.
1270 */
1271static const char *
1272errmsg(int e, const char *em)
1273{
1274 if (e == ENOENT || e == ENOTDIR) {
1275 return em;
1276 }
1277 return strerror(e);
1278}
1279
1280
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001281/* ============ Memory allocation */
1282
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001283#if 0
1284/* I consider these wrappers nearly useless:
1285 * ok, they return you to nearest exception handler, but
1286 * how much memory do you leak in the process, making
1287 * memory starvation worse?
1288 */
1289static void *
1290ckrealloc(void * p, size_t nbytes)
1291{
1292 p = realloc(p, nbytes);
1293 if (!p)
1294 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1295 return p;
1296}
1297
1298static void *
1299ckmalloc(size_t nbytes)
1300{
1301 return ckrealloc(NULL, nbytes);
1302}
1303
1304static void *
1305ckzalloc(size_t nbytes)
1306{
1307 return memset(ckmalloc(nbytes), 0, nbytes);
1308}
1309
1310static char *
1311ckstrdup(const char *s)
1312{
1313 char *p = strdup(s);
1314 if (!p)
1315 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1316 return p;
1317}
1318#else
1319/* Using bbox equivalents. They exit if out of memory */
1320# define ckrealloc xrealloc
1321# define ckmalloc xmalloc
1322# define ckzalloc xzalloc
1323# define ckstrdup xstrdup
1324#endif
1325
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326/*
1327 * It appears that grabstackstr() will barf with such alignments
1328 * because stalloc() will return a string allocated in a new stackblock.
1329 */
1330#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1331enum {
1332 /* Most machines require the value returned from malloc to be aligned
1333 * in some way. The following macro will get this right
1334 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001335 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001336 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001337 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001338};
1339
1340struct stack_block {
1341 struct stack_block *prev;
1342 char space[MINSIZE];
1343};
1344
1345struct stackmark {
1346 struct stack_block *stackp;
1347 char *stacknxt;
1348 size_t stacknleft;
1349 struct stackmark *marknext;
1350};
1351
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001352
Denis Vlasenko01631112007-12-16 17:20:38 +00001353struct globals_memstack {
1354 struct stack_block *g_stackp; // = &stackbase;
1355 struct stackmark *markp;
1356 char *g_stacknxt; // = stackbase.space;
1357 char *sstrend; // = stackbase.space + MINSIZE;
1358 size_t g_stacknleft; // = MINSIZE;
1359 int herefd; // = -1;
1360 struct stack_block stackbase;
1361};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001362extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1363#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001364#define g_stackp (G_memstack.g_stackp )
1365#define markp (G_memstack.markp )
1366#define g_stacknxt (G_memstack.g_stacknxt )
1367#define sstrend (G_memstack.sstrend )
1368#define g_stacknleft (G_memstack.g_stacknleft)
1369#define herefd (G_memstack.herefd )
1370#define stackbase (G_memstack.stackbase )
1371#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001372 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1373 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001374 g_stackp = &stackbase; \
1375 g_stacknxt = stackbase.space; \
1376 g_stacknleft = MINSIZE; \
1377 sstrend = stackbase.space + MINSIZE; \
1378 herefd = -1; \
1379} while (0)
1380
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001381
Denis Vlasenko01631112007-12-16 17:20:38 +00001382#define stackblock() ((void *)g_stacknxt)
1383#define stackblocksize() g_stacknleft
1384
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001385/*
1386 * Parse trees for commands are allocated in lifo order, so we use a stack
1387 * to make this more efficient, and also to avoid all sorts of exception
1388 * handling code to handle interrupts in the middle of a parse.
1389 *
1390 * The size 504 was chosen because the Ultrix malloc handles that size
1391 * well.
1392 */
1393static void *
1394stalloc(size_t nbytes)
1395{
1396 char *p;
1397 size_t aligned;
1398
1399 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001400 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001401 size_t len;
1402 size_t blocksize;
1403 struct stack_block *sp;
1404
1405 blocksize = aligned;
1406 if (blocksize < MINSIZE)
1407 blocksize = MINSIZE;
1408 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1409 if (len < blocksize)
1410 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1411 INT_OFF;
1412 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001413 sp->prev = g_stackp;
1414 g_stacknxt = sp->space;
1415 g_stacknleft = blocksize;
1416 sstrend = g_stacknxt + blocksize;
1417 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001418 INT_ON;
1419 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001420 p = g_stacknxt;
1421 g_stacknxt += aligned;
1422 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001423 return p;
1424}
1425
Denis Vlasenko597906c2008-02-20 16:38:54 +00001426static void *
1427stzalloc(size_t nbytes)
1428{
1429 return memset(stalloc(nbytes), 0, nbytes);
1430}
1431
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001432static void
1433stunalloc(void *p)
1434{
1435#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001436 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001437 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001438 abort();
1439 }
1440#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001441 g_stacknleft += g_stacknxt - (char *)p;
1442 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001443}
1444
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001445/*
1446 * Like strdup but works with the ash stack.
1447 */
1448static char *
1449ststrdup(const char *p)
1450{
1451 size_t len = strlen(p) + 1;
1452 return memcpy(stalloc(len), p, len);
1453}
1454
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001455static void
1456setstackmark(struct stackmark *mark)
1457{
Denis Vlasenko01631112007-12-16 17:20:38 +00001458 mark->stackp = g_stackp;
1459 mark->stacknxt = g_stacknxt;
1460 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001461 mark->marknext = markp;
1462 markp = mark;
1463}
1464
1465static void
1466popstackmark(struct stackmark *mark)
1467{
1468 struct stack_block *sp;
1469
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001470 if (!mark->stackp)
1471 return;
1472
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001473 INT_OFF;
1474 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001475 while (g_stackp != mark->stackp) {
1476 sp = g_stackp;
1477 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001478 free(sp);
1479 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001480 g_stacknxt = mark->stacknxt;
1481 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001482 sstrend = mark->stacknxt + mark->stacknleft;
1483 INT_ON;
1484}
1485
1486/*
1487 * When the parser reads in a string, it wants to stick the string on the
1488 * stack and only adjust the stack pointer when it knows how big the
1489 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1490 * of space on top of the stack and stackblocklen returns the length of
1491 * this block. Growstackblock will grow this space by at least one byte,
1492 * possibly moving it (like realloc). Grabstackblock actually allocates the
1493 * part of the block that has been used.
1494 */
1495static void
1496growstackblock(void)
1497{
1498 size_t newlen;
1499
Denis Vlasenko01631112007-12-16 17:20:38 +00001500 newlen = g_stacknleft * 2;
1501 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001502 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1503 if (newlen < 128)
1504 newlen += 128;
1505
Denis Vlasenko01631112007-12-16 17:20:38 +00001506 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001507 struct stack_block *oldstackp;
1508 struct stackmark *xmark;
1509 struct stack_block *sp;
1510 struct stack_block *prevstackp;
1511 size_t grosslen;
1512
1513 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001514 oldstackp = g_stackp;
1515 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001516 prevstackp = sp->prev;
1517 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1518 sp = ckrealloc(sp, grosslen);
1519 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001520 g_stackp = sp;
1521 g_stacknxt = sp->space;
1522 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001523 sstrend = sp->space + newlen;
1524
1525 /*
1526 * Stack marks pointing to the start of the old block
1527 * must be relocated to point to the new block
1528 */
1529 xmark = markp;
1530 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001531 xmark->stackp = g_stackp;
1532 xmark->stacknxt = g_stacknxt;
1533 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001534 xmark = xmark->marknext;
1535 }
1536 INT_ON;
1537 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001538 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001539 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001540 char *p = stalloc(newlen);
1541
1542 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001543 g_stacknxt = memcpy(p, oldspace, oldlen);
1544 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001545 }
1546}
1547
1548static void
1549grabstackblock(size_t len)
1550{
1551 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001552 g_stacknxt += len;
1553 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001554}
1555
1556/*
1557 * The following routines are somewhat easier to use than the above.
1558 * The user declares a variable of type STACKSTR, which may be declared
1559 * to be a register. The macro STARTSTACKSTR initializes things. Then
1560 * the user uses the macro STPUTC to add characters to the string. In
1561 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1562 * grown as necessary. When the user is done, she can just leave the
1563 * string there and refer to it using stackblock(). Or she can allocate
1564 * the space for it using grabstackstr(). If it is necessary to allow
1565 * someone else to use the stack temporarily and then continue to grow
1566 * the string, the user should use grabstack to allocate the space, and
1567 * then call ungrabstr(p) to return to the previous mode of operation.
1568 *
1569 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1570 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1571 * is space for at least one character.
1572 */
1573static void *
1574growstackstr(void)
1575{
1576 size_t len = stackblocksize();
1577 if (herefd >= 0 && len >= 1024) {
1578 full_write(herefd, stackblock(), len);
1579 return stackblock();
1580 }
1581 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001582 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001583}
1584
1585/*
1586 * Called from CHECKSTRSPACE.
1587 */
1588static char *
1589makestrspace(size_t newlen, char *p)
1590{
Denis Vlasenko01631112007-12-16 17:20:38 +00001591 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001592 size_t size = stackblocksize();
1593
1594 for (;;) {
1595 size_t nleft;
1596
1597 size = stackblocksize();
1598 nleft = size - len;
1599 if (nleft >= newlen)
1600 break;
1601 growstackblock();
1602 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001603 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001604}
1605
1606static char *
1607stack_nputstr(const char *s, size_t n, char *p)
1608{
1609 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001610 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001611 return p;
1612}
1613
1614static char *
1615stack_putstr(const char *s, char *p)
1616{
1617 return stack_nputstr(s, strlen(s), p);
1618}
1619
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001620static char *
1621_STPUTC(int c, char *p)
1622{
1623 if (p == sstrend)
1624 p = growstackstr();
1625 *p++ = c;
1626 return p;
1627}
1628
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001629#define STARTSTACKSTR(p) ((p) = stackblock())
1630#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001631#define CHECKSTRSPACE(n, p) do { \
1632 char *q = (p); \
1633 size_t l = (n); \
1634 size_t m = sstrend - q; \
1635 if (l > m) \
1636 (p) = makestrspace(l, q); \
1637} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001638#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001639#define STACKSTRNUL(p) do { \
1640 if ((p) == sstrend) \
1641 (p) = growstackstr(); \
1642 *(p) = '\0'; \
1643} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001644#define STUNPUTC(p) (--(p))
1645#define STTOPC(p) ((p)[-1])
1646#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001647
1648#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001649#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001650#define stackstrend() ((void *)sstrend)
1651
1652
1653/* ============ String helpers */
1654
1655/*
1656 * prefix -- see if pfx is a prefix of string.
1657 */
1658static char *
1659prefix(const char *string, const char *pfx)
1660{
1661 while (*pfx) {
1662 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001663 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001664 }
1665 return (char *) string;
1666}
1667
1668/*
1669 * Check for a valid number. This should be elsewhere.
1670 */
1671static int
1672is_number(const char *p)
1673{
1674 do {
1675 if (!isdigit(*p))
1676 return 0;
1677 } while (*++p != '\0');
1678 return 1;
1679}
1680
1681/*
1682 * Convert a string of digits to an integer, printing an error message on
1683 * failure.
1684 */
1685static int
1686number(const char *s)
1687{
1688 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001689 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001690 return atoi(s);
1691}
1692
1693/*
1694 * Produce a possibly single quoted string suitable as input to the shell.
1695 * The return string is allocated on the stack.
1696 */
1697static char *
1698single_quote(const char *s)
1699{
1700 char *p;
1701
1702 STARTSTACKSTR(p);
1703
1704 do {
1705 char *q;
1706 size_t len;
1707
1708 len = strchrnul(s, '\'') - s;
1709
1710 q = p = makestrspace(len + 3, p);
1711
1712 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001713 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001714 *q++ = '\'';
1715 s += len;
1716
1717 STADJUST(q - p, p);
1718
Denys Vlasenkocd716832009-11-28 22:14:02 +01001719 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001720 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001721 len = 0;
1722 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723
1724 q = p = makestrspace(len + 3, p);
1725
1726 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001727 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001728 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729
1730 STADJUST(q - p, p);
1731 } while (*s);
1732
Denys Vlasenkocd716832009-11-28 22:14:02 +01001733 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734
1735 return stackblock();
1736}
1737
1738
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001739/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740
1741static char **argptr; /* argument list for builtin commands */
1742static char *optionarg; /* set by nextopt (like getopt) */
1743static char *optptr; /* used by nextopt */
1744
1745/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001746 * XXX - should get rid of. Have all builtins use getopt(3).
1747 * The library getopt must have the BSD extension static variable
1748 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001750 * Standard option processing (a la getopt) for builtin routines.
1751 * The only argument that is passed to nextopt is the option string;
1752 * the other arguments are unnecessary. It returns the character,
1753 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754 */
1755static int
1756nextopt(const char *optstring)
1757{
1758 char *p;
1759 const char *q;
1760 char c;
1761
1762 p = optptr;
1763 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001764 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001766 if (p == NULL)
1767 return '\0';
1768 if (*p != '-')
1769 return '\0';
1770 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001771 return '\0';
1772 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001773 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001775 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001777 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001778 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001779 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001781 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001782 if (*++q == ':')
1783 q++;
1784 }
1785 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001786 if (*p == '\0') {
1787 p = *argptr++;
1788 if (p == NULL)
1789 ash_msg_and_raise_error("no arg for -%c option", c);
1790 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001791 optionarg = p;
1792 p = NULL;
1793 }
1794 optptr = p;
1795 return c;
1796}
1797
1798
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001799/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001800
Denis Vlasenko01631112007-12-16 17:20:38 +00001801/*
1802 * The parsefile structure pointed to by the global variable parsefile
1803 * contains information about the current file being read.
1804 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001805struct shparam {
1806 int nparam; /* # of positional parameters (without $0) */
1807#if ENABLE_ASH_GETOPTS
1808 int optind; /* next parameter to be processed by getopts */
1809 int optoff; /* used by getopts */
1810#endif
1811 unsigned char malloced; /* if parameter list dynamically allocated */
1812 char **p; /* parameter list */
1813};
1814
1815/*
1816 * Free the list of positional parameters.
1817 */
1818static void
1819freeparam(volatile struct shparam *param)
1820{
Denis Vlasenko01631112007-12-16 17:20:38 +00001821 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001822 char **ap, **ap1;
1823 ap = ap1 = param->p;
1824 while (*ap)
1825 free(*ap++);
1826 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001827 }
1828}
1829
1830#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001831static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001832#endif
1833
1834struct var {
1835 struct var *next; /* next entry in hash list */
1836 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001837 const char *var_text; /* name=value */
1838 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001839 /* the variable gets set/unset */
1840};
1841
1842struct localvar {
1843 struct localvar *next; /* next local variable in list */
1844 struct var *vp; /* the variable that was made local */
1845 int flags; /* saved flags */
1846 const char *text; /* saved text */
1847};
1848
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001849/* flags */
1850#define VEXPORT 0x01 /* variable is exported */
1851#define VREADONLY 0x02 /* variable cannot be modified */
1852#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1853#define VTEXTFIXED 0x08 /* text is statically allocated */
1854#define VSTACK 0x10 /* text is allocated on the stack */
1855#define VUNSET 0x20 /* the variable is not set */
1856#define VNOFUNC 0x40 /* don't call the callback function */
1857#define VNOSET 0x80 /* do not set variable - just readonly test */
1858#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001859#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001860# define VDYNAMIC 0x200 /* dynamic variable */
1861#else
1862# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001863#endif
1864
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001865
Denis Vlasenko01631112007-12-16 17:20:38 +00001866/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001867#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001868static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001869change_lc_all(const char *value)
1870{
1871 if (value && *value != '\0')
1872 setlocale(LC_ALL, value);
1873}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001874static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001875change_lc_ctype(const char *value)
1876{
1877 if (value && *value != '\0')
1878 setlocale(LC_CTYPE, value);
1879}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001880#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001881#if ENABLE_ASH_MAIL
1882static void chkmail(void);
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001883static void changemail(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001884#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001885static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001886#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001887static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001888#endif
1889
Denis Vlasenko01631112007-12-16 17:20:38 +00001890static const struct {
1891 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001892 const char *var_text;
1893 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001894} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001895 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001896#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001897 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1898 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001899#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001900 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1901 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1902 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1903 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001904#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001905 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001906#endif
1907#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001908 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001909#endif
1910#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001911 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1912 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001913#endif
1914#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001915 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001916#endif
1917};
1918
Denis Vlasenko0b769642008-07-24 07:54:57 +00001919struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001920
1921struct globals_var {
1922 struct shparam shellparam; /* $@ current positional parameters */
1923 struct redirtab *redirlist;
1924 int g_nullredirs;
1925 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1926 struct var *vartab[VTABSIZE];
1927 struct var varinit[ARRAY_SIZE(varinit_data)];
1928};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001929extern struct globals_var *const ash_ptr_to_globals_var;
1930#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001931#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001932//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001933#define g_nullredirs (G_var.g_nullredirs )
1934#define preverrout_fd (G_var.preverrout_fd)
1935#define vartab (G_var.vartab )
1936#define varinit (G_var.varinit )
1937#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001938 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001939 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1940 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001941 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001942 varinit[i].flags = varinit_data[i].flags; \
1943 varinit[i].var_text = varinit_data[i].var_text; \
1944 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001945 } \
1946} while (0)
1947
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001948#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001949#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001950# define vmail (&vifs)[1]
1951# define vmpath (&vmail)[1]
1952# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001953#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001954# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001955#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001956#define vps1 (&vpath)[1]
1957#define vps2 (&vps1)[1]
1958#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001959#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001960# define voptind (&vps4)[1]
1961# if ENABLE_ASH_RANDOM_SUPPORT
1962# define vrandom (&voptind)[1]
1963# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001965# if ENABLE_ASH_RANDOM_SUPPORT
1966# define vrandom (&vps4)[1]
1967# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001968#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001969
1970/*
1971 * The following macros access the values of the above variables.
1972 * They have to skip over the name. They return the null string
1973 * for unset variables.
1974 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001975#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001976#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001977#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001978# define mailval() (vmail.var_text + 5)
1979# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001980# define mpathset() ((vmpath.flags & VUNSET) == 0)
1981#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001982#define pathval() (vpath.var_text + 5)
1983#define ps1val() (vps1.var_text + 4)
1984#define ps2val() (vps2.var_text + 4)
1985#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001986#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001987# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001988#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001989
Denis Vlasenko01631112007-12-16 17:20:38 +00001990#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001991static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001992getoptsreset(const char *value)
1993{
1994 shellparam.optind = number(value);
1995 shellparam.optoff = -1;
1996}
1997#endif
1998
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02001999/* math.h has these, otherwise define our private copies */
2000#if !ENABLE_SH_MATH_SUPPORT
2001#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2002#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002003/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002004 * Return the pointer to the first char which is not part of a legal variable name
2005 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002006 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002007static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002008endofname(const char *name)
2009{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002010 if (!is_name(*name))
2011 return name;
2012 while (*++name) {
2013 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002014 break;
2015 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002016 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002017}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002018#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002019
2020/*
2021 * Compares two strings up to the first = or '\0'. The first
2022 * string must be terminated by '='; the second may be terminated by
2023 * either '=' or '\0'.
2024 */
2025static int
2026varcmp(const char *p, const char *q)
2027{
2028 int c, d;
2029
2030 while ((c = *p) == (d = *q)) {
2031 if (!c || c == '=')
2032 goto out;
2033 p++;
2034 q++;
2035 }
2036 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002037 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002038 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002039 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002040 out:
2041 return c - d;
2042}
2043
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002044/*
2045 * Find the appropriate entry in the hash table from the name.
2046 */
2047static struct var **
2048hashvar(const char *p)
2049{
2050 unsigned hashval;
2051
2052 hashval = ((unsigned char) *p) << 4;
2053 while (*p && *p != '=')
2054 hashval += (unsigned char) *p++;
2055 return &vartab[hashval % VTABSIZE];
2056}
2057
2058static int
2059vpcmp(const void *a, const void *b)
2060{
2061 return varcmp(*(const char **)a, *(const char **)b);
2062}
2063
2064/*
2065 * This routine initializes the builtin variables.
2066 */
2067static void
2068initvar(void)
2069{
2070 struct var *vp;
2071 struct var *end;
2072 struct var **vpp;
2073
2074 /*
2075 * PS1 depends on uid
2076 */
2077#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002078 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079#else
2080 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002081 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002082#endif
2083 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002084 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002085 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002086 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002087 vp->next = *vpp;
2088 *vpp = vp;
2089 } while (++vp < end);
2090}
2091
2092static struct var **
2093findvar(struct var **vpp, const char *name)
2094{
2095 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002096 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002097 break;
2098 }
2099 }
2100 return vpp;
2101}
2102
2103/*
2104 * Find the value of a variable. Returns NULL if not set.
2105 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002106static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002107lookupvar(const char *name)
2108{
2109 struct var *v;
2110
2111 v = *findvar(hashvar(name), name);
2112 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002113#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002114 /*
2115 * Dynamic variables are implemented roughly the same way they are
2116 * in bash. Namely, they're "special" so long as they aren't unset.
2117 * As soon as they're unset, they're no longer dynamic, and dynamic
2118 * lookup will no longer happen at that point. -- PFM.
2119 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002120 if (v->flags & VDYNAMIC)
2121 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002122#endif
2123 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002124 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002125 }
2126 return NULL;
2127}
2128
2129/*
2130 * Search the environment of a builtin command.
2131 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002132static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002133bltinlookup(const char *name)
2134{
2135 struct strlist *sp;
2136
2137 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002138 if (varcmp(sp->text, name) == 0)
2139 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002140 }
2141 return lookupvar(name);
2142}
2143
2144/*
2145 * Same as setvar except that the variable and value are passed in
2146 * the first argument as name=value. Since the first argument will
2147 * be actually stored in the table, it should not be a string that
2148 * will go away.
2149 * Called with interrupts off.
2150 */
2151static void
2152setvareq(char *s, int flags)
2153{
2154 struct var *vp, **vpp;
2155
2156 vpp = hashvar(s);
2157 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2158 vp = *findvar(vpp, s);
2159 if (vp) {
2160 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2161 const char *n;
2162
2163 if (flags & VNOSAVE)
2164 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002165 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002166 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2167 }
2168
2169 if (flags & VNOSET)
2170 return;
2171
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002172 if (vp->var_func && !(flags & VNOFUNC))
2173 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002174
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002175 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2176 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002177
2178 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2179 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002180 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002181 if (flags & VNOSET)
2182 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002183 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002184 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002185 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186 *vpp = vp;
2187 }
2188 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2189 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002190 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002191 vp->flags = flags;
2192}
2193
2194/*
2195 * Set the value of a variable. The flags argument is ored with the
2196 * flags of the variable. If val is NULL, the variable is unset.
2197 */
2198static void
2199setvar(const char *name, const char *val, int flags)
2200{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002201 const char *q;
2202 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002203 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002204 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002205 size_t vallen;
2206
2207 q = endofname(name);
2208 p = strchrnul(q, '=');
2209 namelen = p - name;
2210 if (!namelen || p != q)
2211 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2212 vallen = 0;
2213 if (val == NULL) {
2214 flags |= VUNSET;
2215 } else {
2216 vallen = strlen(val);
2217 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002218
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002219 INT_OFF;
2220 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002221 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002222 if (val) {
2223 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002224 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002225 }
2226 *p = '\0';
2227 setvareq(nameeq, flags | VNOSAVE);
2228 INT_ON;
2229}
2230
Denys Vlasenko03dad222010-01-12 23:29:57 +01002231static void FAST_FUNC
2232setvar2(const char *name, const char *val)
2233{
2234 setvar(name, val, 0);
2235}
2236
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002237#if ENABLE_ASH_GETOPTS
2238/*
2239 * Safe version of setvar, returns 1 on success 0 on failure.
2240 */
2241static int
2242setvarsafe(const char *name, const char *val, int flags)
2243{
2244 int err;
2245 volatile int saveint;
2246 struct jmploc *volatile savehandler = exception_handler;
2247 struct jmploc jmploc;
2248
2249 SAVE_INT(saveint);
2250 if (setjmp(jmploc.loc))
2251 err = 1;
2252 else {
2253 exception_handler = &jmploc;
2254 setvar(name, val, flags);
2255 err = 0;
2256 }
2257 exception_handler = savehandler;
2258 RESTORE_INT(saveint);
2259 return err;
2260}
2261#endif
2262
2263/*
2264 * Unset the specified variable.
2265 */
2266static int
2267unsetvar(const char *s)
2268{
2269 struct var **vpp;
2270 struct var *vp;
2271 int retval;
2272
2273 vpp = findvar(hashvar(s), s);
2274 vp = *vpp;
2275 retval = 2;
2276 if (vp) {
2277 int flags = vp->flags;
2278
2279 retval = 1;
2280 if (flags & VREADONLY)
2281 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002282#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002283 vp->flags &= ~VDYNAMIC;
2284#endif
2285 if (flags & VUNSET)
2286 goto ok;
2287 if ((flags & VSTRFIXED) == 0) {
2288 INT_OFF;
2289 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002290 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002291 *vpp = vp->next;
2292 free(vp);
2293 INT_ON;
2294 } else {
2295 setvar(s, 0, 0);
2296 vp->flags &= ~VEXPORT;
2297 }
2298 ok:
2299 retval = 0;
2300 }
2301 out:
2302 return retval;
2303}
2304
2305/*
2306 * Process a linked list of variable assignments.
2307 */
2308static void
2309listsetvar(struct strlist *list_set_var, int flags)
2310{
2311 struct strlist *lp = list_set_var;
2312
2313 if (!lp)
2314 return;
2315 INT_OFF;
2316 do {
2317 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002318 lp = lp->next;
2319 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002320 INT_ON;
2321}
2322
2323/*
2324 * Generate a list of variables satisfying the given conditions.
2325 */
2326static char **
2327listvars(int on, int off, char ***end)
2328{
2329 struct var **vpp;
2330 struct var *vp;
2331 char **ep;
2332 int mask;
2333
2334 STARTSTACKSTR(ep);
2335 vpp = vartab;
2336 mask = on | off;
2337 do {
2338 for (vp = *vpp; vp; vp = vp->next) {
2339 if ((vp->flags & mask) == on) {
2340 if (ep == stackstrend())
2341 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002342 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002343 }
2344 }
2345 } while (++vpp < vartab + VTABSIZE);
2346 if (ep == stackstrend())
2347 ep = growstackstr();
2348 if (end)
2349 *end = ep;
2350 *ep++ = NULL;
2351 return grabstackstr(ep);
2352}
2353
2354
2355/* ============ Path search helper
2356 *
2357 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002358 * of the path before the first call; path_advance will update
2359 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002360 * the possible path expansions in sequence. If an option (indicated by
2361 * a percent sign) appears in the path entry then the global variable
2362 * pathopt will be set to point to it; otherwise pathopt will be set to
2363 * NULL.
2364 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002365static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002366
2367static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002368path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002369{
2370 const char *p;
2371 char *q;
2372 const char *start;
2373 size_t len;
2374
2375 if (*path == NULL)
2376 return NULL;
2377 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002378 for (p = start; *p && *p != ':' && *p != '%'; p++)
2379 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002380 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2381 while (stackblocksize() < len)
2382 growstackblock();
2383 q = stackblock();
2384 if (p != start) {
2385 memcpy(q, start, p - start);
2386 q += p - start;
2387 *q++ = '/';
2388 }
2389 strcpy(q, name);
2390 pathopt = NULL;
2391 if (*p == '%') {
2392 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002393 while (*p && *p != ':')
2394 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002395 }
2396 if (*p == ':')
2397 *path = p + 1;
2398 else
2399 *path = NULL;
2400 return stalloc(len);
2401}
2402
2403
2404/* ============ Prompt */
2405
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002406static smallint doprompt; /* if set, prompt the user */
2407static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002408
2409#if ENABLE_FEATURE_EDITING
2410static line_input_t *line_input_state;
2411static const char *cmdedit_prompt;
2412static void
2413putprompt(const char *s)
2414{
2415 if (ENABLE_ASH_EXPAND_PRMT) {
2416 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002417 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002418 return;
2419 }
2420 cmdedit_prompt = s;
2421}
2422#else
2423static void
2424putprompt(const char *s)
2425{
2426 out2str(s);
2427}
2428#endif
2429
2430#if ENABLE_ASH_EXPAND_PRMT
2431/* expandstr() needs parsing machinery, so it is far away ahead... */
2432static const char *expandstr(const char *ps);
2433#else
2434#define expandstr(s) s
2435#endif
2436
2437static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002438setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002439{
2440 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002441 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2442
2443 if (!do_set)
2444 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002445
2446 needprompt = 0;
2447
2448 switch (whichprompt) {
2449 case 1:
2450 prompt = ps1val();
2451 break;
2452 case 2:
2453 prompt = ps2val();
2454 break;
2455 default: /* 0 */
2456 prompt = nullstr;
2457 }
2458#if ENABLE_ASH_EXPAND_PRMT
2459 setstackmark(&smark);
2460 stalloc(stackblocksize());
2461#endif
2462 putprompt(expandstr(prompt));
2463#if ENABLE_ASH_EXPAND_PRMT
2464 popstackmark(&smark);
2465#endif
2466}
2467
2468
2469/* ============ The cd and pwd commands */
2470
2471#define CD_PHYSICAL 1
2472#define CD_PRINT 2
2473
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002474static int
2475cdopt(void)
2476{
2477 int flags = 0;
2478 int i, j;
2479
2480 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002481 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002482 if (i != j) {
2483 flags ^= CD_PHYSICAL;
2484 j = i;
2485 }
2486 }
2487
2488 return flags;
2489}
2490
2491/*
2492 * Update curdir (the name of the current directory) in response to a
2493 * cd command.
2494 */
2495static const char *
2496updatepwd(const char *dir)
2497{
2498 char *new;
2499 char *p;
2500 char *cdcomppath;
2501 const char *lim;
2502
2503 cdcomppath = ststrdup(dir);
2504 STARTSTACKSTR(new);
2505 if (*dir != '/') {
2506 if (curdir == nullstr)
2507 return 0;
2508 new = stack_putstr(curdir, new);
2509 }
2510 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002511 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002512 if (*dir != '/') {
2513 if (new[-1] != '/')
2514 USTPUTC('/', new);
2515 if (new > lim && *lim == '/')
2516 lim++;
2517 } else {
2518 USTPUTC('/', new);
2519 cdcomppath++;
2520 if (dir[1] == '/' && dir[2] != '/') {
2521 USTPUTC('/', new);
2522 cdcomppath++;
2523 lim++;
2524 }
2525 }
2526 p = strtok(cdcomppath, "/");
2527 while (p) {
2528 switch (*p) {
2529 case '.':
2530 if (p[1] == '.' && p[2] == '\0') {
2531 while (new > lim) {
2532 STUNPUTC(new);
2533 if (new[-1] == '/')
2534 break;
2535 }
2536 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002537 }
2538 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002539 break;
2540 /* fall through */
2541 default:
2542 new = stack_putstr(p, new);
2543 USTPUTC('/', new);
2544 }
2545 p = strtok(0, "/");
2546 }
2547 if (new > lim)
2548 STUNPUTC(new);
2549 *new = 0;
2550 return stackblock();
2551}
2552
2553/*
2554 * Find out what the current directory is. If we already know the current
2555 * directory, this routine returns immediately.
2556 */
2557static char *
2558getpwd(void)
2559{
Denis Vlasenko01631112007-12-16 17:20:38 +00002560 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002561 return dir ? dir : nullstr;
2562}
2563
2564static void
2565setpwd(const char *val, int setold)
2566{
2567 char *oldcur, *dir;
2568
2569 oldcur = dir = curdir;
2570
2571 if (setold) {
2572 setvar("OLDPWD", oldcur, VEXPORT);
2573 }
2574 INT_OFF;
2575 if (physdir != nullstr) {
2576 if (physdir != oldcur)
2577 free(physdir);
2578 physdir = nullstr;
2579 }
2580 if (oldcur == val || !val) {
2581 char *s = getpwd();
2582 physdir = s;
2583 if (!val)
2584 dir = s;
2585 } else
2586 dir = ckstrdup(val);
2587 if (oldcur != dir && oldcur != nullstr) {
2588 free(oldcur);
2589 }
2590 curdir = dir;
2591 INT_ON;
2592 setvar("PWD", dir, VEXPORT);
2593}
2594
2595static void hashcd(void);
2596
2597/*
2598 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2599 * know that the current directory has changed.
2600 */
2601static int
2602docd(const char *dest, int flags)
2603{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002604 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002605 int err;
2606
2607 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2608
2609 INT_OFF;
2610 if (!(flags & CD_PHYSICAL)) {
2611 dir = updatepwd(dest);
2612 if (dir)
2613 dest = dir;
2614 }
2615 err = chdir(dest);
2616 if (err)
2617 goto out;
2618 setpwd(dir, 1);
2619 hashcd();
2620 out:
2621 INT_ON;
2622 return err;
2623}
2624
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002625static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002626cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002627{
2628 const char *dest;
2629 const char *path;
2630 const char *p;
2631 char c;
2632 struct stat statb;
2633 int flags;
2634
2635 flags = cdopt();
2636 dest = *argptr;
2637 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002638 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002639 else if (LONE_DASH(dest)) {
2640 dest = bltinlookup("OLDPWD");
2641 flags |= CD_PRINT;
2642 }
2643 if (!dest)
2644 dest = nullstr;
2645 if (*dest == '/')
2646 goto step7;
2647 if (*dest == '.') {
2648 c = dest[1];
2649 dotdot:
2650 switch (c) {
2651 case '\0':
2652 case '/':
2653 goto step6;
2654 case '.':
2655 c = dest[2];
2656 if (c != '.')
2657 goto dotdot;
2658 }
2659 }
2660 if (!*dest)
2661 dest = ".";
2662 path = bltinlookup("CDPATH");
2663 if (!path) {
2664 step6:
2665 step7:
2666 p = dest;
2667 goto docd;
2668 }
2669 do {
2670 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002671 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002672 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2673 if (c && c != ':')
2674 flags |= CD_PRINT;
2675 docd:
2676 if (!docd(p, flags))
2677 goto out;
2678 break;
2679 }
2680 } while (path);
2681 ash_msg_and_raise_error("can't cd to %s", dest);
2682 /* NOTREACHED */
2683 out:
2684 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002685 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002686 return 0;
2687}
2688
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002689static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002690pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002691{
2692 int flags;
2693 const char *dir = curdir;
2694
2695 flags = cdopt();
2696 if (flags) {
2697 if (physdir == nullstr)
2698 setpwd(dir, 0);
2699 dir = physdir;
2700 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002701 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002702 return 0;
2703}
2704
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002705
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002706/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002707
Denis Vlasenko834dee72008-10-07 09:18:30 +00002708
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002709#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002710
Eric Andersenc470f442003-07-28 09:56:35 +00002711/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002712#define CWORD 0 /* character is nothing special */
2713#define CNL 1 /* newline character */
2714#define CBACK 2 /* a backslash character */
2715#define CSQUOTE 3 /* single quote */
2716#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002717#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002718#define CBQUOTE 6 /* backwards single quote */
2719#define CVAR 7 /* a dollar sign */
2720#define CENDVAR 8 /* a '}' character */
2721#define CLP 9 /* a left paren in arithmetic */
2722#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002723#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002724#define CCTL 12 /* like CWORD, except it must be escaped */
2725#define CSPCL 13 /* these terminate a word */
2726#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002727
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002728#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002729#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002730# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002731#endif
2732
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002733#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002734
Mike Frysinger98c52642009-04-02 10:02:37 +00002735#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002736# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002737#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002738# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002739#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002740static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002741#if ENABLE_ASH_ALIAS
2742 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2743#endif
2744 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2745 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2746 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2747 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2748 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2749 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2750 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2751 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2752 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2753 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2754 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002755#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002756 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2757 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2758 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2759#endif
2760#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002761};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002762/* Constants below must match table above */
2763enum {
2764#if ENABLE_ASH_ALIAS
2765 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2766#endif
2767 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2768 CNL_CNL_CNL_CNL , /* 2 */
2769 CWORD_CCTL_CCTL_CWORD , /* 3 */
2770 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2771 CVAR_CVAR_CWORD_CVAR , /* 5 */
2772 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2773 CSPCL_CWORD_CWORD_CLP , /* 7 */
2774 CSPCL_CWORD_CWORD_CRP , /* 8 */
2775 CBACK_CBACK_CCTL_CBACK , /* 9 */
2776 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2777 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2778 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2779 CWORD_CWORD_CWORD_CWORD , /* 13 */
2780 CCTL_CCTL_CCTL_CCTL , /* 14 */
2781};
Eric Andersen2870d962001-07-02 17:27:21 +00002782
Denys Vlasenkocd716832009-11-28 22:14:02 +01002783/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2784 * caller must ensure proper cast on it if c is *char_ptr!
2785 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002786/* Values for syntax param */
2787#define BASESYNTAX 0 /* not in quotes */
2788#define DQSYNTAX 1 /* in double quotes */
2789#define SQSYNTAX 2 /* in single quotes */
2790#define ARISYNTAX 3 /* in arithmetic */
2791#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002792
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002793#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002794
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002795static int
2796SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002797{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002798 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002799# if ENABLE_ASH_ALIAS
2800 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002801 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2802 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2803 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2804 11, 3 /* "}~" */
2805 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002806# else
2807 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002808 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2809 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2810 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2811 10, 2 /* "}~" */
2812 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002813# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002814 const char *s;
2815 int indx;
2816
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002817 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002818 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002819# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002820 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002821 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002822 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002823# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002824 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002825 /* Cast is purely for paranoia here,
2826 * just in case someone passed signed char to us */
2827 if ((unsigned char)c >= CTL_FIRST
2828 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002829 ) {
2830 return CCTL;
2831 }
2832 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002833 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002834 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002835 indx = syntax_index_table[s - spec_symbls];
2836 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002837 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002838}
2839
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002840#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002841
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002842static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002843 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002844 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2854 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2855 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2878 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2879 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2881 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2883 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2884 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2885 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2886 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2887 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2889 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2891 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2892 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2903 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2904 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2905 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2907 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2937 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2938 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2941 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2969 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2970 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2971 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2972 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2973 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2974 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2975 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2976 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2977 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2978 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2979 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2980 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2981 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2982 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2983 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2986 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 150 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 151 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 152 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 153 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 154 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003100 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003101# if ENABLE_ASH_ALIAS
3102 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3103# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003104};
3105
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003106# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003107
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003108#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003109
Eric Andersen2870d962001-07-02 17:27:21 +00003110
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003111/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003112
Denis Vlasenko131ae172007-02-18 13:00:19 +00003113#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003114
3115#define ALIASINUSE 1
3116#define ALIASDEAD 2
3117
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003118struct alias {
3119 struct alias *next;
3120 char *name;
3121 char *val;
3122 int flag;
3123};
3124
Denis Vlasenko01631112007-12-16 17:20:38 +00003125
3126static struct alias **atab; // [ATABSIZE];
3127#define INIT_G_alias() do { \
3128 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3129} while (0)
3130
Eric Andersen2870d962001-07-02 17:27:21 +00003131
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003132static struct alias **
3133__lookupalias(const char *name) {
3134 unsigned int hashval;
3135 struct alias **app;
3136 const char *p;
3137 unsigned int ch;
3138
3139 p = name;
3140
3141 ch = (unsigned char)*p;
3142 hashval = ch << 4;
3143 while (ch) {
3144 hashval += ch;
3145 ch = (unsigned char)*++p;
3146 }
3147 app = &atab[hashval % ATABSIZE];
3148
3149 for (; *app; app = &(*app)->next) {
3150 if (strcmp(name, (*app)->name) == 0) {
3151 break;
3152 }
3153 }
3154
3155 return app;
3156}
3157
3158static struct alias *
3159lookupalias(const char *name, int check)
3160{
3161 struct alias *ap = *__lookupalias(name);
3162
3163 if (check && ap && (ap->flag & ALIASINUSE))
3164 return NULL;
3165 return ap;
3166}
3167
3168static struct alias *
3169freealias(struct alias *ap)
3170{
3171 struct alias *next;
3172
3173 if (ap->flag & ALIASINUSE) {
3174 ap->flag |= ALIASDEAD;
3175 return ap;
3176 }
3177
3178 next = ap->next;
3179 free(ap->name);
3180 free(ap->val);
3181 free(ap);
3182 return next;
3183}
Eric Andersencb57d552001-06-28 07:25:16 +00003184
Eric Andersenc470f442003-07-28 09:56:35 +00003185static void
3186setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003187{
3188 struct alias *ap, **app;
3189
3190 app = __lookupalias(name);
3191 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003192 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003193 if (ap) {
3194 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003195 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003196 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003197 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003198 ap->flag &= ~ALIASDEAD;
3199 } else {
3200 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003201 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003202 ap->name = ckstrdup(name);
3203 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003204 /*ap->flag = 0; - ckzalloc did it */
3205 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003206 *app = ap;
3207 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003208 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003209}
3210
Eric Andersenc470f442003-07-28 09:56:35 +00003211static int
3212unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003213{
Eric Andersencb57d552001-06-28 07:25:16 +00003214 struct alias **app;
3215
3216 app = __lookupalias(name);
3217
3218 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003219 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003220 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003221 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003222 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003223 }
3224
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003225 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003226}
3227
Eric Andersenc470f442003-07-28 09:56:35 +00003228static void
3229rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003230{
Eric Andersencb57d552001-06-28 07:25:16 +00003231 struct alias *ap, **app;
3232 int i;
3233
Denis Vlasenkob012b102007-02-19 22:43:01 +00003234 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003235 for (i = 0; i < ATABSIZE; i++) {
3236 app = &atab[i];
3237 for (ap = *app; ap; ap = *app) {
3238 *app = freealias(*app);
3239 if (ap == *app) {
3240 app = &ap->next;
3241 }
3242 }
3243 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003244 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003245}
3246
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003247static void
3248printalias(const struct alias *ap)
3249{
3250 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3251}
3252
Eric Andersencb57d552001-06-28 07:25:16 +00003253/*
3254 * TODO - sort output
3255 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003256static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003257aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003258{
3259 char *n, *v;
3260 int ret = 0;
3261 struct alias *ap;
3262
Denis Vlasenko68404f12008-03-17 09:00:54 +00003263 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003264 int i;
3265
Denis Vlasenko68404f12008-03-17 09:00:54 +00003266 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003267 for (ap = atab[i]; ap; ap = ap->next) {
3268 printalias(ap);
3269 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003270 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003271 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003272 }
3273 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003274 v = strchr(n+1, '=');
3275 if (v == NULL) { /* n+1: funny ksh stuff */
3276 ap = *__lookupalias(n);
3277 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003278 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003279 ret = 1;
3280 } else
3281 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003282 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003283 *v++ = '\0';
3284 setalias(n, v);
3285 }
3286 }
3287
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003288 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003289}
3290
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003291static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003292unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003293{
3294 int i;
3295
3296 while ((i = nextopt("a")) != '\0') {
3297 if (i == 'a') {
3298 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003299 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003300 }
3301 }
3302 for (i = 0; *argptr; argptr++) {
3303 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003304 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003305 i = 1;
3306 }
3307 }
3308
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003309 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003310}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003311
Denis Vlasenko131ae172007-02-18 13:00:19 +00003312#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003313
Eric Andersenc470f442003-07-28 09:56:35 +00003314
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003315/* ============ jobs.c */
3316
3317/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003318#define FORK_FG 0
3319#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320#define FORK_NOJOB 2
3321
3322/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003323#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3324#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3325#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003326
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327/*
3328 * A job structure contains information about a job. A job is either a
3329 * single process or a set of processes contained in a pipeline. In the
3330 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3331 * array of pids.
3332 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003333struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003334 pid_t ps_pid; /* process id */
3335 int ps_status; /* last process status from wait() */
3336 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003337};
3338
3339struct job {
3340 struct procstat ps0; /* status of process */
3341 struct procstat *ps; /* status or processes when more than one */
3342#if JOBS
3343 int stopstatus; /* status of a stopped job */
3344#endif
3345 uint32_t
3346 nprocs: 16, /* number of processes */
3347 state: 8,
3348#define JOBRUNNING 0 /* at least one proc running */
3349#define JOBSTOPPED 1 /* all procs are stopped */
3350#define JOBDONE 2 /* all procs are completed */
3351#if JOBS
3352 sigint: 1, /* job was killed by SIGINT */
3353 jobctl: 1, /* job running under job control */
3354#endif
3355 waited: 1, /* true if this entry has been waited for */
3356 used: 1, /* true if this entry is in used */
3357 changed: 1; /* true if status has changed */
3358 struct job *prev_job; /* previous job */
3359};
3360
Denis Vlasenko68404f12008-03-17 09:00:54 +00003361static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362static int forkshell(struct job *, union node *, int);
3363static int waitforjob(struct job *);
3364
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003365#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003366enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003367#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003368#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003369static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003370static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003371#endif
3372
3373/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003374 * Ignore a signal.
3375 */
3376static void
3377ignoresig(int signo)
3378{
3379 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3380 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3381 /* No, need to do it */
3382 signal(signo, SIG_IGN);
3383 }
3384 sigmode[signo - 1] = S_HARD_IGN;
3385}
3386
3387/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003388 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003389 */
3390static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003391signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003392{
3393 gotsig[signo - 1] = 1;
3394
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003395 if (signo == SIGINT && !trap[SIGINT]) {
3396 if (!suppress_int) {
3397 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003398 raise_interrupt(); /* does not return */
3399 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003400 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003401 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003402 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003403 }
3404}
3405
3406/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003407 * Set the signal handler for the specified signal. The routine figures
3408 * out what it should be set to.
3409 */
3410static void
3411setsignal(int signo)
3412{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003413 char *t;
3414 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003415 struct sigaction act;
3416
3417 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003418 new_act = S_DFL;
3419 if (t != NULL) { /* trap for this sig is set */
3420 new_act = S_CATCH;
3421 if (t[0] == '\0') /* trap is "": ignore this sig */
3422 new_act = S_IGN;
3423 }
3424
3425 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003426 switch (signo) {
3427 case SIGINT:
3428 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003429 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003430 break;
3431 case SIGQUIT:
3432#if DEBUG
3433 if (debug)
3434 break;
3435#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003436 /* man bash:
3437 * "In all cases, bash ignores SIGQUIT. Non-builtin
3438 * commands run by bash have signal handlers
3439 * set to the values inherited by the shell
3440 * from its parent". */
3441 new_act = S_IGN;
3442 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003443 case SIGTERM:
3444 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003445 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003446 break;
3447#if JOBS
3448 case SIGTSTP:
3449 case SIGTTOU:
3450 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003451 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003452 break;
3453#endif
3454 }
3455 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003456//TODO: if !rootshell, we reset SIGQUIT to DFL,
3457//whereas we have to restore it to what shell got on entry
3458//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003459
3460 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003461 cur_act = *t;
3462 if (cur_act == 0) {
3463 /* current setting is not yet known */
3464 if (sigaction(signo, NULL, &act)) {
3465 /* pretend it worked; maybe we should give a warning,
3466 * but other shells don't. We don't alter sigmode,
3467 * so we retry every time.
3468 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003469 return;
3470 }
3471 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003472 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003473 if (mflag
3474 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3475 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003476 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003477 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003478 }
3479 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003480 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003481 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003482
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003483 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003484 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003485 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003486 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003487 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3488 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003489 break;
3490 case S_IGN:
3491 act.sa_handler = SIG_IGN;
3492 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003493 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003494 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003495
3496 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003497}
3498
3499/* mode flags for set_curjob */
3500#define CUR_DELETE 2
3501#define CUR_RUNNING 1
3502#define CUR_STOPPED 0
3503
3504/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003505#define DOWAIT_NONBLOCK WNOHANG
3506#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003507
3508#if JOBS
3509/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003510static int initialpgrp; //references:2
3511static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003512#endif
3513/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003514static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003515/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003516static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003517/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003518static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003519/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003520static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003521
3522static void
3523set_curjob(struct job *jp, unsigned mode)
3524{
3525 struct job *jp1;
3526 struct job **jpp, **curp;
3527
3528 /* first remove from list */
3529 jpp = curp = &curjob;
3530 do {
3531 jp1 = *jpp;
3532 if (jp1 == jp)
3533 break;
3534 jpp = &jp1->prev_job;
3535 } while (1);
3536 *jpp = jp1->prev_job;
3537
3538 /* Then re-insert in correct position */
3539 jpp = curp;
3540 switch (mode) {
3541 default:
3542#if DEBUG
3543 abort();
3544#endif
3545 case CUR_DELETE:
3546 /* job being deleted */
3547 break;
3548 case CUR_RUNNING:
3549 /* newly created job or backgrounded job,
3550 put after all stopped jobs. */
3551 do {
3552 jp1 = *jpp;
3553#if JOBS
3554 if (!jp1 || jp1->state != JOBSTOPPED)
3555#endif
3556 break;
3557 jpp = &jp1->prev_job;
3558 } while (1);
3559 /* FALLTHROUGH */
3560#if JOBS
3561 case CUR_STOPPED:
3562#endif
3563 /* newly stopped job - becomes curjob */
3564 jp->prev_job = *jpp;
3565 *jpp = jp;
3566 break;
3567 }
3568}
3569
3570#if JOBS || DEBUG
3571static int
3572jobno(const struct job *jp)
3573{
3574 return jp - jobtab + 1;
3575}
3576#endif
3577
3578/*
3579 * Convert a job name to a job structure.
3580 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003581#if !JOBS
3582#define getjob(name, getctl) getjob(name)
3583#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003584static struct job *
3585getjob(const char *name, int getctl)
3586{
3587 struct job *jp;
3588 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003589 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003590 unsigned num;
3591 int c;
3592 const char *p;
3593 char *(*match)(const char *, const char *);
3594
3595 jp = curjob;
3596 p = name;
3597 if (!p)
3598 goto currentjob;
3599
3600 if (*p != '%')
3601 goto err;
3602
3603 c = *++p;
3604 if (!c)
3605 goto currentjob;
3606
3607 if (!p[1]) {
3608 if (c == '+' || c == '%') {
3609 currentjob:
3610 err_msg = "No current job";
3611 goto check;
3612 }
3613 if (c == '-') {
3614 if (jp)
3615 jp = jp->prev_job;
3616 err_msg = "No previous job";
3617 check:
3618 if (!jp)
3619 goto err;
3620 goto gotit;
3621 }
3622 }
3623
3624 if (is_number(p)) {
3625 num = atoi(p);
3626 if (num < njobs) {
3627 jp = jobtab + num - 1;
3628 if (jp->used)
3629 goto gotit;
3630 goto err;
3631 }
3632 }
3633
3634 match = prefix;
3635 if (*p == '?') {
3636 match = strstr;
3637 p++;
3638 }
3639
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003640 found = NULL;
3641 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003642 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003643 if (found)
3644 goto err;
3645 found = jp;
3646 err_msg = "%s: ambiguous";
3647 }
3648 jp = jp->prev_job;
3649 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003650 if (!found)
3651 goto err;
3652 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003653
3654 gotit:
3655#if JOBS
3656 err_msg = "job %s not created under job control";
3657 if (getctl && jp->jobctl == 0)
3658 goto err;
3659#endif
3660 return jp;
3661 err:
3662 ash_msg_and_raise_error(err_msg, name);
3663}
3664
3665/*
3666 * Mark a job structure as unused.
3667 */
3668static void
3669freejob(struct job *jp)
3670{
3671 struct procstat *ps;
3672 int i;
3673
3674 INT_OFF;
3675 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003676 if (ps->ps_cmd != nullstr)
3677 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003678 }
3679 if (jp->ps != &jp->ps0)
3680 free(jp->ps);
3681 jp->used = 0;
3682 set_curjob(jp, CUR_DELETE);
3683 INT_ON;
3684}
3685
3686#if JOBS
3687static void
3688xtcsetpgrp(int fd, pid_t pgrp)
3689{
3690 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003691 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003692}
3693
3694/*
3695 * Turn job control on and off.
3696 *
3697 * Note: This code assumes that the third arg to ioctl is a character
3698 * pointer, which is true on Berkeley systems but not System V. Since
3699 * System V doesn't have job control yet, this isn't a problem now.
3700 *
3701 * Called with interrupts off.
3702 */
3703static void
3704setjobctl(int on)
3705{
3706 int fd;
3707 int pgrp;
3708
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003709 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003710 return;
3711 if (on) {
3712 int ofd;
3713 ofd = fd = open(_PATH_TTY, O_RDWR);
3714 if (fd < 0) {
3715 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3716 * That sometimes helps to acquire controlling tty.
3717 * Obviously, a workaround for bugs when someone
3718 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003719 fd = 2;
3720 while (!isatty(fd))
3721 if (--fd < 0)
3722 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003723 }
3724 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003725 if (ofd >= 0)
3726 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003727 if (fd < 0)
3728 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003729 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003730 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003731 do { /* while we are in the background */
3732 pgrp = tcgetpgrp(fd);
3733 if (pgrp < 0) {
3734 out:
3735 ash_msg("can't access tty; job control turned off");
3736 mflag = on = 0;
3737 goto close;
3738 }
3739 if (pgrp == getpgrp())
3740 break;
3741 killpg(0, SIGTTIN);
3742 } while (1);
3743 initialpgrp = pgrp;
3744
3745 setsignal(SIGTSTP);
3746 setsignal(SIGTTOU);
3747 setsignal(SIGTTIN);
3748 pgrp = rootpid;
3749 setpgid(0, pgrp);
3750 xtcsetpgrp(fd, pgrp);
3751 } else {
3752 /* turning job control off */
3753 fd = ttyfd;
3754 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003755 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003756 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003757 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003758 setpgid(0, pgrp);
3759 setsignal(SIGTSTP);
3760 setsignal(SIGTTOU);
3761 setsignal(SIGTTIN);
3762 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003763 if (fd >= 0)
3764 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765 fd = -1;
3766 }
3767 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003768 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003769}
3770
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003771static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003772killcmd(int argc, char **argv)
3773{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003774 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003775 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003776 do {
3777 if (argv[i][0] == '%') {
3778 struct job *jp = getjob(argv[i], 0);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003779 unsigned pid = jp->ps[0].ps_pid;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003780 /* Enough space for ' -NNN<nul>' */
3781 argv[i] = alloca(sizeof(int)*3 + 3);
3782 /* kill_main has matching code to expect
3783 * leading space. Needed to not confuse
3784 * negative pids with "kill -SIGNAL_NO" syntax */
3785 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003787 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003789 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790}
3791
3792static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003793showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003794{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003795 struct procstat *ps;
3796 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003797
Denys Vlasenko285ad152009-12-04 23:02:27 +01003798 psend = jp->ps + jp->nprocs;
3799 for (ps = jp->ps + 1; ps < psend; ps++)
3800 printf(" | %s", ps->ps_cmd);
3801 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802 flush_stdout_stderr();
3803}
3804
3805
3806static int
3807restartjob(struct job *jp, int mode)
3808{
3809 struct procstat *ps;
3810 int i;
3811 int status;
3812 pid_t pgid;
3813
3814 INT_OFF;
3815 if (jp->state == JOBDONE)
3816 goto out;
3817 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003818 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003819 if (mode == FORK_FG)
3820 xtcsetpgrp(ttyfd, pgid);
3821 killpg(pgid, SIGCONT);
3822 ps = jp->ps;
3823 i = jp->nprocs;
3824 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003825 if (WIFSTOPPED(ps->ps_status)) {
3826 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003827 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003828 ps++;
3829 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003830 out:
3831 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3832 INT_ON;
3833 return status;
3834}
3835
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003836static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003837fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003838{
3839 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840 int mode;
3841 int retval;
3842
3843 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3844 nextopt(nullstr);
3845 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003846 do {
3847 jp = getjob(*argv, 1);
3848 if (mode == FORK_BG) {
3849 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003850 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003851 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003852 out1str(jp->ps[0].ps_cmd);
3853 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003854 retval = restartjob(jp, mode);
3855 } while (*argv && *++argv);
3856 return retval;
3857}
3858#endif
3859
3860static int
3861sprint_status(char *s, int status, int sigonly)
3862{
3863 int col;
3864 int st;
3865
3866 col = 0;
3867 if (!WIFEXITED(status)) {
3868#if JOBS
3869 if (WIFSTOPPED(status))
3870 st = WSTOPSIG(status);
3871 else
3872#endif
3873 st = WTERMSIG(status);
3874 if (sigonly) {
3875 if (st == SIGINT || st == SIGPIPE)
3876 goto out;
3877#if JOBS
3878 if (WIFSTOPPED(status))
3879 goto out;
3880#endif
3881 }
3882 st &= 0x7f;
3883 col = fmtstr(s, 32, strsignal(st));
3884 if (WCOREDUMP(status)) {
3885 col += fmtstr(s + col, 16, " (core dumped)");
3886 }
3887 } else if (!sigonly) {
3888 st = WEXITSTATUS(status);
3889 if (st)
3890 col = fmtstr(s, 16, "Done(%d)", st);
3891 else
3892 col = fmtstr(s, 16, "Done");
3893 }
3894 out:
3895 return col;
3896}
3897
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003898static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003899dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003900{
3901 int pid;
3902 int status;
3903 struct job *jp;
3904 struct job *thisjob;
3905 int state;
3906
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003907 TRACE(("dowait(0x%x) called\n", wait_flags));
3908
3909 /* Do a wait system call. If job control is compiled in, we accept
3910 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3911 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003912 if (doing_jobctl)
3913 wait_flags |= WUNTRACED;
3914 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003915 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3916 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003917 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003918 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003919
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003920 INT_OFF;
3921 thisjob = NULL;
3922 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003923 struct procstat *ps;
3924 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003925 if (jp->state == JOBDONE)
3926 continue;
3927 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003928 ps = jp->ps;
3929 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003930 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003931 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932 TRACE(("Job %d: changing status of proc %d "
3933 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003934 jobno(jp), pid, ps->ps_status, status));
3935 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003936 thisjob = jp;
3937 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003938 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003939 state = JOBRUNNING;
3940#if JOBS
3941 if (state == JOBRUNNING)
3942 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003943 if (WIFSTOPPED(ps->ps_status)) {
3944 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003945 state = JOBSTOPPED;
3946 }
3947#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003948 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 if (thisjob)
3950 goto gotjob;
3951 }
3952#if JOBS
3953 if (!WIFSTOPPED(status))
3954#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003955 jobless--;
3956 goto out;
3957
3958 gotjob:
3959 if (state != JOBRUNNING) {
3960 thisjob->changed = 1;
3961
3962 if (thisjob->state != state) {
3963 TRACE(("Job %d: changing state from %d to %d\n",
3964 jobno(thisjob), thisjob->state, state));
3965 thisjob->state = state;
3966#if JOBS
3967 if (state == JOBSTOPPED) {
3968 set_curjob(thisjob, CUR_STOPPED);
3969 }
3970#endif
3971 }
3972 }
3973
3974 out:
3975 INT_ON;
3976
3977 if (thisjob && thisjob == job) {
3978 char s[48 + 1];
3979 int len;
3980
3981 len = sprint_status(s, status, 1);
3982 if (len) {
3983 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003984 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003985 out2str(s);
3986 }
3987 }
3988 return pid;
3989}
3990
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003991static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003992blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003993{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003994 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003995 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003996 raise_exception(EXSIG);
3997 return pid;
3998}
3999
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004000#if JOBS
4001static void
4002showjob(FILE *out, struct job *jp, int mode)
4003{
4004 struct procstat *ps;
4005 struct procstat *psend;
4006 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004007 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004008 char s[80];
4009
4010 ps = jp->ps;
4011
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004012 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004013 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004014 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004015 return;
4016 }
4017
4018 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004019 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004020
4021 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004022 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004023 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004024 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004025
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004026 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004027 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004028
4029 psend = ps + jp->nprocs;
4030
4031 if (jp->state == JOBRUNNING) {
4032 strcpy(s + col, "Running");
4033 col += sizeof("Running") - 1;
4034 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004035 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004036 if (jp->state == JOBSTOPPED)
4037 status = jp->stopstatus;
4038 col += sprint_status(s + col, status, 0);
4039 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004040 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004042 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4043 * or prints several "PID | <cmdN>" lines,
4044 * depending on SHOW_PIDS bit.
4045 * We do not print status of individual processes
4046 * between PID and <cmdN>. bash does it, but not very well:
4047 * first line shows overall job status, not process status,
4048 * making it impossible to know 1st process status.
4049 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004051 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004053 s[0] = '\0';
4054 col = 33;
4055 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004056 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004057 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004058 fprintf(out, "%s%*c%s%s",
4059 s,
4060 33 - col >= 0 ? 33 - col : 0, ' ',
4061 ps == jp->ps ? "" : "| ",
4062 ps->ps_cmd
4063 );
4064 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004065 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004066
4067 jp->changed = 0;
4068
4069 if (jp->state == JOBDONE) {
4070 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4071 freejob(jp);
4072 }
4073}
4074
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004075/*
4076 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4077 * statuses have changed since the last call to showjobs.
4078 */
4079static void
4080showjobs(FILE *out, int mode)
4081{
4082 struct job *jp;
4083
Denys Vlasenko883cea42009-07-11 15:31:59 +02004084 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004085
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004086 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004087 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 continue;
4089
4090 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004091 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004092 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004093 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004094 }
4095}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004096
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004097static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004098jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004099{
4100 int mode, m;
4101
4102 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004103 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004104 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004105 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004106 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004107 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004108 }
4109
4110 argv = argptr;
4111 if (*argv) {
4112 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004113 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004114 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004115 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004116 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004117 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004118
4119 return 0;
4120}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004121#endif /* JOBS */
4122
Michael Abbott359da5e2009-12-04 23:03:29 +01004123/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004124static int
4125getstatus(struct job *job)
4126{
4127 int status;
4128 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004129 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004130
Michael Abbott359da5e2009-12-04 23:03:29 +01004131 /* Fetch last member's status */
4132 ps = job->ps + job->nprocs - 1;
4133 status = ps->ps_status;
4134 if (pipefail) {
4135 /* "set -o pipefail" mode: use last _nonzero_ status */
4136 while (status == 0 && --ps >= job->ps)
4137 status = ps->ps_status;
4138 }
4139
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004140 retval = WEXITSTATUS(status);
4141 if (!WIFEXITED(status)) {
4142#if JOBS
4143 retval = WSTOPSIG(status);
4144 if (!WIFSTOPPED(status))
4145#endif
4146 {
4147 /* XXX: limits number of signals */
4148 retval = WTERMSIG(status);
4149#if JOBS
4150 if (retval == SIGINT)
4151 job->sigint = 1;
4152#endif
4153 }
4154 retval += 128;
4155 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004156 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004157 jobno(job), job->nprocs, status, retval));
4158 return retval;
4159}
4160
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004161static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004162waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004163{
4164 struct job *job;
4165 int retval;
4166 struct job *jp;
4167
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004168 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004169 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004170
4171 nextopt(nullstr);
4172 retval = 0;
4173
4174 argv = argptr;
4175 if (!*argv) {
4176 /* wait for all jobs */
4177 for (;;) {
4178 jp = curjob;
4179 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004180 if (!jp) /* no running procs */
4181 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004182 if (jp->state == JOBRUNNING)
4183 break;
4184 jp->waited = 1;
4185 jp = jp->prev_job;
4186 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004187 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004188 /* man bash:
4189 * "When bash is waiting for an asynchronous command via
4190 * the wait builtin, the reception of a signal for which a trap
4191 * has been set will cause the wait builtin to return immediately
4192 * with an exit status greater than 128, immediately after which
4193 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004194 *
4195 * blocking_wait_with_raise_on_sig raises signal handlers
4196 * if it gets no pid (pid < 0). However,
4197 * if child sends us a signal *and immediately exits*,
4198 * blocking_wait_with_raise_on_sig gets pid > 0
4199 * and does not handle pending_sig. Check this case: */
4200 if (pending_sig)
4201 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004202 }
4203 }
4204
4205 retval = 127;
4206 do {
4207 if (**argv != '%') {
4208 pid_t pid = number(*argv);
4209 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004210 while (1) {
4211 if (!job)
4212 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004213 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214 break;
4215 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004216 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004217 } else
4218 job = getjob(*argv, 0);
4219 /* loop until process terminated or stopped */
4220 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004221 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004222 job->waited = 1;
4223 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004224 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004225 } while (*++argv);
4226
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004227 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004228 return retval;
4229}
4230
4231static struct job *
4232growjobtab(void)
4233{
4234 size_t len;
4235 ptrdiff_t offset;
4236 struct job *jp, *jq;
4237
4238 len = njobs * sizeof(*jp);
4239 jq = jobtab;
4240 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4241
4242 offset = (char *)jp - (char *)jq;
4243 if (offset) {
4244 /* Relocate pointers */
4245 size_t l = len;
4246
4247 jq = (struct job *)((char *)jq + l);
4248 while (l) {
4249 l -= sizeof(*jp);
4250 jq--;
4251#define joff(p) ((struct job *)((char *)(p) + l))
4252#define jmove(p) (p) = (void *)((char *)(p) + offset)
4253 if (joff(jp)->ps == &jq->ps0)
4254 jmove(joff(jp)->ps);
4255 if (joff(jp)->prev_job)
4256 jmove(joff(jp)->prev_job);
4257 }
4258 if (curjob)
4259 jmove(curjob);
4260#undef joff
4261#undef jmove
4262 }
4263
4264 njobs += 4;
4265 jobtab = jp;
4266 jp = (struct job *)((char *)jp + len);
4267 jq = jp + 3;
4268 do {
4269 jq->used = 0;
4270 } while (--jq >= jp);
4271 return jp;
4272}
4273
4274/*
4275 * Return a new job structure.
4276 * Called with interrupts off.
4277 */
4278static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004279makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004280{
4281 int i;
4282 struct job *jp;
4283
4284 for (i = njobs, jp = jobtab; ; jp++) {
4285 if (--i < 0) {
4286 jp = growjobtab();
4287 break;
4288 }
4289 if (jp->used == 0)
4290 break;
4291 if (jp->state != JOBDONE || !jp->waited)
4292 continue;
4293#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004294 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004295 continue;
4296#endif
4297 freejob(jp);
4298 break;
4299 }
4300 memset(jp, 0, sizeof(*jp));
4301#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004302 /* jp->jobctl is a bitfield.
4303 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004304 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004305 jp->jobctl = 1;
4306#endif
4307 jp->prev_job = curjob;
4308 curjob = jp;
4309 jp->used = 1;
4310 jp->ps = &jp->ps0;
4311 if (nprocs > 1) {
4312 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4313 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004314 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004315 jobno(jp)));
4316 return jp;
4317}
4318
4319#if JOBS
4320/*
4321 * Return a string identifying a command (to be printed by the
4322 * jobs command).
4323 */
4324static char *cmdnextc;
4325
4326static void
4327cmdputs(const char *s)
4328{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004329 static const char vstype[VSTYPE + 1][3] = {
4330 "", "}", "-", "+", "?", "=",
4331 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004332 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004333 };
4334
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004335 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004336 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004337 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004338 unsigned char c;
4339 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004340 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004341
Denys Vlasenko46a14772009-12-10 21:27:13 +01004342 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004343 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4344 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004345 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004346 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004347 switch (c) {
4348 case CTLESC:
4349 c = *p++;
4350 break;
4351 case CTLVAR:
4352 subtype = *p++;
4353 if ((subtype & VSTYPE) == VSLENGTH)
4354 str = "${#";
4355 else
4356 str = "${";
4357 if (!(subtype & VSQUOTE) == !(quoted & 1))
4358 goto dostr;
4359 quoted ^= 1;
4360 c = '"';
4361 break;
4362 case CTLENDVAR:
4363 str = "\"}" + !(quoted & 1);
4364 quoted >>= 1;
4365 subtype = 0;
4366 goto dostr;
4367 case CTLBACKQ:
4368 str = "$(...)";
4369 goto dostr;
4370 case CTLBACKQ+CTLQUOTE:
4371 str = "\"$(...)\"";
4372 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004373#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004374 case CTLARI:
4375 str = "$((";
4376 goto dostr;
4377 case CTLENDARI:
4378 str = "))";
4379 goto dostr;
4380#endif
4381 case CTLQUOTEMARK:
4382 quoted ^= 1;
4383 c = '"';
4384 break;
4385 case '=':
4386 if (subtype == 0)
4387 break;
4388 if ((subtype & VSTYPE) != VSNORMAL)
4389 quoted <<= 1;
4390 str = vstype[subtype & VSTYPE];
4391 if (subtype & VSNUL)
4392 c = ':';
4393 else
4394 goto checkstr;
4395 break;
4396 case '\'':
4397 case '\\':
4398 case '"':
4399 case '$':
4400 /* These can only happen inside quotes */
4401 cc[0] = c;
4402 str = cc;
4403 c = '\\';
4404 break;
4405 default:
4406 break;
4407 }
4408 USTPUTC(c, nextc);
4409 checkstr:
4410 if (!str)
4411 continue;
4412 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004413 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004414 USTPUTC(c, nextc);
4415 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004416 } /* while *p++ not NUL */
4417
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004418 if (quoted & 1) {
4419 USTPUTC('"', nextc);
4420 }
4421 *nextc = 0;
4422 cmdnextc = nextc;
4423}
4424
4425/* cmdtxt() and cmdlist() call each other */
4426static void cmdtxt(union node *n);
4427
4428static void
4429cmdlist(union node *np, int sep)
4430{
4431 for (; np; np = np->narg.next) {
4432 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004433 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004434 cmdtxt(np);
4435 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004436 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004437 }
4438}
4439
4440static void
4441cmdtxt(union node *n)
4442{
4443 union node *np;
4444 struct nodelist *lp;
4445 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004446
4447 if (!n)
4448 return;
4449 switch (n->type) {
4450 default:
4451#if DEBUG
4452 abort();
4453#endif
4454 case NPIPE:
4455 lp = n->npipe.cmdlist;
4456 for (;;) {
4457 cmdtxt(lp->n);
4458 lp = lp->next;
4459 if (!lp)
4460 break;
4461 cmdputs(" | ");
4462 }
4463 break;
4464 case NSEMI:
4465 p = "; ";
4466 goto binop;
4467 case NAND:
4468 p = " && ";
4469 goto binop;
4470 case NOR:
4471 p = " || ";
4472 binop:
4473 cmdtxt(n->nbinary.ch1);
4474 cmdputs(p);
4475 n = n->nbinary.ch2;
4476 goto donode;
4477 case NREDIR:
4478 case NBACKGND:
4479 n = n->nredir.n;
4480 goto donode;
4481 case NNOT:
4482 cmdputs("!");
4483 n = n->nnot.com;
4484 donode:
4485 cmdtxt(n);
4486 break;
4487 case NIF:
4488 cmdputs("if ");
4489 cmdtxt(n->nif.test);
4490 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004491 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004492 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493 cmdputs("; else ");
4494 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004495 } else {
4496 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004497 }
4498 p = "; fi";
4499 goto dotail;
4500 case NSUBSHELL:
4501 cmdputs("(");
4502 n = n->nredir.n;
4503 p = ")";
4504 goto dotail;
4505 case NWHILE:
4506 p = "while ";
4507 goto until;
4508 case NUNTIL:
4509 p = "until ";
4510 until:
4511 cmdputs(p);
4512 cmdtxt(n->nbinary.ch1);
4513 n = n->nbinary.ch2;
4514 p = "; done";
4515 dodo:
4516 cmdputs("; do ");
4517 dotail:
4518 cmdtxt(n);
4519 goto dotail2;
4520 case NFOR:
4521 cmdputs("for ");
4522 cmdputs(n->nfor.var);
4523 cmdputs(" in ");
4524 cmdlist(n->nfor.args, 1);
4525 n = n->nfor.body;
4526 p = "; done";
4527 goto dodo;
4528 case NDEFUN:
4529 cmdputs(n->narg.text);
4530 p = "() { ... }";
4531 goto dotail2;
4532 case NCMD:
4533 cmdlist(n->ncmd.args, 1);
4534 cmdlist(n->ncmd.redirect, 0);
4535 break;
4536 case NARG:
4537 p = n->narg.text;
4538 dotail2:
4539 cmdputs(p);
4540 break;
4541 case NHERE:
4542 case NXHERE:
4543 p = "<<...";
4544 goto dotail2;
4545 case NCASE:
4546 cmdputs("case ");
4547 cmdputs(n->ncase.expr->narg.text);
4548 cmdputs(" in ");
4549 for (np = n->ncase.cases; np; np = np->nclist.next) {
4550 cmdtxt(np->nclist.pattern);
4551 cmdputs(") ");
4552 cmdtxt(np->nclist.body);
4553 cmdputs(";; ");
4554 }
4555 p = "esac";
4556 goto dotail2;
4557 case NTO:
4558 p = ">";
4559 goto redir;
4560 case NCLOBBER:
4561 p = ">|";
4562 goto redir;
4563 case NAPPEND:
4564 p = ">>";
4565 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004566#if ENABLE_ASH_BASH_COMPAT
4567 case NTO2:
4568#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004569 case NTOFD:
4570 p = ">&";
4571 goto redir;
4572 case NFROM:
4573 p = "<";
4574 goto redir;
4575 case NFROMFD:
4576 p = "<&";
4577 goto redir;
4578 case NFROMTO:
4579 p = "<>";
4580 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004581 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004582 cmdputs(p);
4583 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004584 cmdputs(utoa(n->ndup.dupfd));
4585 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004586 }
4587 n = n->nfile.fname;
4588 goto donode;
4589 }
4590}
4591
4592static char *
4593commandtext(union node *n)
4594{
4595 char *name;
4596
4597 STARTSTACKSTR(cmdnextc);
4598 cmdtxt(n);
4599 name = stackblock();
4600 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4601 name, cmdnextc, cmdnextc));
4602 return ckstrdup(name);
4603}
4604#endif /* JOBS */
4605
4606/*
4607 * Fork off a subshell. If we are doing job control, give the subshell its
4608 * own process group. Jp is a job structure that the job is to be added to.
4609 * N is the command that will be evaluated by the child. Both jp and n may
4610 * be NULL. The mode parameter can be one of the following:
4611 * FORK_FG - Fork off a foreground process.
4612 * FORK_BG - Fork off a background process.
4613 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4614 * process group even if job control is on.
4615 *
4616 * When job control is turned off, background processes have their standard
4617 * input redirected to /dev/null (except for the second and later processes
4618 * in a pipeline).
4619 *
4620 * Called with interrupts off.
4621 */
4622/*
4623 * Clear traps on a fork.
4624 */
4625static void
4626clear_traps(void)
4627{
4628 char **tp;
4629
4630 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004631 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004632 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004633 if (trap_ptr == trap)
4634 free(*tp);
4635 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004636 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004637 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004638 setsignal(tp - trap);
4639 INT_ON;
4640 }
4641 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004642 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004643}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004644
4645/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004646static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004647
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004648/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004649static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004650forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004651{
4652 int oldlvl;
4653
4654 TRACE(("Child shell %d\n", getpid()));
4655 oldlvl = shlvl;
4656 shlvl++;
4657
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004658 /* man bash: "Non-builtin commands run by bash have signal handlers
4659 * set to the values inherited by the shell from its parent".
4660 * Do we do it correctly? */
4661
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004662 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004663
4664 if (mode == FORK_NOJOB /* is it `xxx` ? */
4665 && n && n->type == NCMD /* is it single cmd? */
4666 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004667 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004668 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4669 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4670 ) {
4671 TRACE(("Trap hack\n"));
4672 /* Awful hack for `trap` or $(trap).
4673 *
4674 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4675 * contains an example where "trap" is executed in a subshell:
4676 *
4677 * save_traps=$(trap)
4678 * ...
4679 * eval "$save_traps"
4680 *
4681 * Standard does not say that "trap" in subshell shall print
4682 * parent shell's traps. It only says that its output
4683 * must have suitable form, but then, in the above example
4684 * (which is not supposed to be normative), it implies that.
4685 *
4686 * bash (and probably other shell) does implement it
4687 * (traps are reset to defaults, but "trap" still shows them),
4688 * but as a result, "trap" logic is hopelessly messed up:
4689 *
4690 * # trap
4691 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4692 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4693 * # true | trap <--- trap is in subshell - no output (ditto)
4694 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4695 * trap -- 'echo Ho' SIGWINCH
4696 * # echo `(trap)` <--- in subshell in subshell - output
4697 * trap -- 'echo Ho' SIGWINCH
4698 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4699 * trap -- 'echo Ho' SIGWINCH
4700 *
4701 * The rules when to forget and when to not forget traps
4702 * get really complex and nonsensical.
4703 *
4704 * Our solution: ONLY bare $(trap) or `trap` is special.
4705 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004706 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004707 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004708 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004709 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004710 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004711#if JOBS
4712 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004713 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004714 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4715 pid_t pgrp;
4716
4717 if (jp->nprocs == 0)
4718 pgrp = getpid();
4719 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004720 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004721 /* this can fail because we are doing it in the parent also */
4722 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004723 if (mode == FORK_FG)
4724 xtcsetpgrp(ttyfd, pgrp);
4725 setsignal(SIGTSTP);
4726 setsignal(SIGTTOU);
4727 } else
4728#endif
4729 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004730 /* man bash: "When job control is not in effect,
4731 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004732 ignoresig(SIGINT);
4733 ignoresig(SIGQUIT);
4734 if (jp->nprocs == 0) {
4735 close(0);
4736 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004737 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004738 }
4739 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004740 if (!oldlvl) {
4741 if (iflag) { /* why if iflag only? */
4742 setsignal(SIGINT);
4743 setsignal(SIGTERM);
4744 }
4745 /* man bash:
4746 * "In all cases, bash ignores SIGQUIT. Non-builtin
4747 * commands run by bash have signal handlers
4748 * set to the values inherited by the shell
4749 * from its parent".
4750 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004751 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004752 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004753#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004754 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004755 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004756 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004757 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004758 /* "jobs": we do not want to clear job list for it,
4759 * instead we remove only _its_ own_ job from job list.
4760 * This makes "jobs .... | cat" more useful.
4761 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004762 freejob(curjob);
4763 return;
4764 }
4765#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004766 for (jp = curjob; jp; jp = jp->prev_job)
4767 freejob(jp);
4768 jobless = 0;
4769}
4770
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004771/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004772#if !JOBS
4773#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4774#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004775static void
4776forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4777{
4778 TRACE(("In parent shell: child = %d\n", pid));
4779 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004780 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4781 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004782 jobless++;
4783 return;
4784 }
4785#if JOBS
4786 if (mode != FORK_NOJOB && jp->jobctl) {
4787 int pgrp;
4788
4789 if (jp->nprocs == 0)
4790 pgrp = pid;
4791 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004792 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004793 /* This can fail because we are doing it in the child also */
4794 setpgid(pid, pgrp);
4795 }
4796#endif
4797 if (mode == FORK_BG) {
4798 backgndpid = pid; /* set $! */
4799 set_curjob(jp, CUR_RUNNING);
4800 }
4801 if (jp) {
4802 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004803 ps->ps_pid = pid;
4804 ps->ps_status = -1;
4805 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004806#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004807 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004808 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004809#endif
4810 }
4811}
4812
4813static int
4814forkshell(struct job *jp, union node *n, int mode)
4815{
4816 int pid;
4817
4818 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4819 pid = fork();
4820 if (pid < 0) {
4821 TRACE(("Fork failed, errno=%d", errno));
4822 if (jp)
4823 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004824 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004825 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004826 if (pid == 0) {
4827 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004828 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004829 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004830 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004831 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004832 return pid;
4833}
4834
4835/*
4836 * Wait for job to finish.
4837 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004838 * Under job control we have the problem that while a child process
4839 * is running interrupts generated by the user are sent to the child
4840 * but not to the shell. This means that an infinite loop started by
4841 * an interactive user may be hard to kill. With job control turned off,
4842 * an interactive user may place an interactive program inside a loop.
4843 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004844 * these interrupts to also abort the loop. The approach we take here
4845 * is to have the shell ignore interrupt signals while waiting for a
4846 * foreground process to terminate, and then send itself an interrupt
4847 * signal if the child process was terminated by an interrupt signal.
4848 * Unfortunately, some programs want to do a bit of cleanup and then
4849 * exit on interrupt; unless these processes terminate themselves by
4850 * sending a signal to themselves (instead of calling exit) they will
4851 * confuse this approach.
4852 *
4853 * Called with interrupts off.
4854 */
4855static int
4856waitforjob(struct job *jp)
4857{
4858 int st;
4859
4860 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004861
4862 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004863 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004864 /* In non-interactive shells, we _can_ get
4865 * a keyboard signal here and be EINTRed,
4866 * but we just loop back, waiting for command to complete.
4867 *
4868 * man bash:
4869 * "If bash is waiting for a command to complete and receives
4870 * a signal for which a trap has been set, the trap
4871 * will not be executed until the command completes."
4872 *
4873 * Reality is that even if trap is not set, bash
4874 * will not act on the signal until command completes.
4875 * Try this. sleep5intoff.c:
4876 * #include <signal.h>
4877 * #include <unistd.h>
4878 * int main() {
4879 * sigset_t set;
4880 * sigemptyset(&set);
4881 * sigaddset(&set, SIGINT);
4882 * sigaddset(&set, SIGQUIT);
4883 * sigprocmask(SIG_BLOCK, &set, NULL);
4884 * sleep(5);
4885 * return 0;
4886 * }
4887 * $ bash -c './sleep5intoff; echo hi'
4888 * ^C^C^C^C <--- pressing ^C once a second
4889 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004890 * $ bash -c './sleep5intoff; echo hi'
4891 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4892 * $ _
4893 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004894 dowait(DOWAIT_BLOCK, jp);
4895 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004896 INT_ON;
4897
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004898 st = getstatus(jp);
4899#if JOBS
4900 if (jp->jobctl) {
4901 xtcsetpgrp(ttyfd, rootpid);
4902 /*
4903 * This is truly gross.
4904 * If we're doing job control, then we did a TIOCSPGRP which
4905 * caused us (the shell) to no longer be in the controlling
4906 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4907 * intuit from the subprocess exit status whether a SIGINT
4908 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4909 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004910 if (jp->sigint) /* TODO: do the same with all signals */
4911 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004912 }
4913 if (jp->state == JOBDONE)
4914#endif
4915 freejob(jp);
4916 return st;
4917}
4918
4919/*
4920 * return 1 if there are stopped jobs, otherwise 0
4921 */
4922static int
4923stoppedjobs(void)
4924{
4925 struct job *jp;
4926 int retval;
4927
4928 retval = 0;
4929 if (job_warning)
4930 goto out;
4931 jp = curjob;
4932 if (jp && jp->state == JOBSTOPPED) {
4933 out2str("You have stopped jobs.\n");
4934 job_warning = 2;
4935 retval++;
4936 }
4937 out:
4938 return retval;
4939}
4940
4941
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004942/* ============ redir.c
4943 *
4944 * Code for dealing with input/output redirection.
4945 */
4946
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004947#undef EMPTY
4948#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004949#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004950#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004951
4952/*
4953 * Open a file in noclobber mode.
4954 * The code was copied from bash.
4955 */
4956static int
4957noclobberopen(const char *fname)
4958{
4959 int r, fd;
4960 struct stat finfo, finfo2;
4961
4962 /*
4963 * If the file exists and is a regular file, return an error
4964 * immediately.
4965 */
4966 r = stat(fname, &finfo);
4967 if (r == 0 && S_ISREG(finfo.st_mode)) {
4968 errno = EEXIST;
4969 return -1;
4970 }
4971
4972 /*
4973 * If the file was not present (r != 0), make sure we open it
4974 * exclusively so that if it is created before we open it, our open
4975 * will fail. Make sure that we do not truncate an existing file.
4976 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4977 * file was not a regular file, we leave O_EXCL off.
4978 */
4979 if (r != 0)
4980 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4981 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4982
4983 /* If the open failed, return the file descriptor right away. */
4984 if (fd < 0)
4985 return fd;
4986
4987 /*
4988 * OK, the open succeeded, but the file may have been changed from a
4989 * non-regular file to a regular file between the stat and the open.
4990 * We are assuming that the O_EXCL open handles the case where FILENAME
4991 * did not exist and is symlinked to an existing file between the stat
4992 * and open.
4993 */
4994
4995 /*
4996 * If we can open it and fstat the file descriptor, and neither check
4997 * revealed that it was a regular file, and the file has not been
4998 * replaced, return the file descriptor.
4999 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005000 if (fstat(fd, &finfo2) == 0
5001 && !S_ISREG(finfo2.st_mode)
5002 && finfo.st_dev == finfo2.st_dev
5003 && finfo.st_ino == finfo2.st_ino
5004 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005005 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005006 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005007
5008 /* The file has been replaced. badness. */
5009 close(fd);
5010 errno = EEXIST;
5011 return -1;
5012}
5013
5014/*
5015 * Handle here documents. Normally we fork off a process to write the
5016 * data to a pipe. If the document is short, we can stuff the data in
5017 * the pipe without forking.
5018 */
5019/* openhere needs this forward reference */
5020static void expandhere(union node *arg, int fd);
5021static int
5022openhere(union node *redir)
5023{
5024 int pip[2];
5025 size_t len = 0;
5026
5027 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005028 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005029 if (redir->type == NHERE) {
5030 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005031 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005032 full_write(pip[1], redir->nhere.doc->narg.text, len);
5033 goto out;
5034 }
5035 }
5036 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005037 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005038 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005039 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5040 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5041 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5042 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005043 signal(SIGPIPE, SIG_DFL);
5044 if (redir->type == NHERE)
5045 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005046 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005047 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005048 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005049 }
5050 out:
5051 close(pip[1]);
5052 return pip[0];
5053}
5054
5055static int
5056openredirect(union node *redir)
5057{
5058 char *fname;
5059 int f;
5060
5061 switch (redir->nfile.type) {
5062 case NFROM:
5063 fname = redir->nfile.expfname;
5064 f = open(fname, O_RDONLY);
5065 if (f < 0)
5066 goto eopen;
5067 break;
5068 case NFROMTO:
5069 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005070 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005071 if (f < 0)
5072 goto ecreate;
5073 break;
5074 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005075#if ENABLE_ASH_BASH_COMPAT
5076 case NTO2:
5077#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005078 /* Take care of noclobber mode. */
5079 if (Cflag) {
5080 fname = redir->nfile.expfname;
5081 f = noclobberopen(fname);
5082 if (f < 0)
5083 goto ecreate;
5084 break;
5085 }
5086 /* FALLTHROUGH */
5087 case NCLOBBER:
5088 fname = redir->nfile.expfname;
5089 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5090 if (f < 0)
5091 goto ecreate;
5092 break;
5093 case NAPPEND:
5094 fname = redir->nfile.expfname;
5095 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5096 if (f < 0)
5097 goto ecreate;
5098 break;
5099 default:
5100#if DEBUG
5101 abort();
5102#endif
5103 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005104/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005105// case NTOFD:
5106// case NFROMFD:
5107// f = -1;
5108// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005109 case NHERE:
5110 case NXHERE:
5111 f = openhere(redir);
5112 break;
5113 }
5114
5115 return f;
5116 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005117 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005118 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005119 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005120}
5121
5122/*
5123 * Copy a file descriptor to be >= to. Returns -1
5124 * if the source file descriptor is closed, EMPTY if there are no unused
5125 * file descriptors left.
5126 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005127/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5128 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005129enum {
5130 COPYFD_EXACT = (int)~(INT_MAX),
5131 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5132};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005133static int
5134copyfd(int from, int to)
5135{
5136 int newfd;
5137
Denis Vlasenko5a867312008-07-24 19:46:38 +00005138 if (to & COPYFD_EXACT) {
5139 to &= ~COPYFD_EXACT;
5140 /*if (from != to)*/
5141 newfd = dup2(from, to);
5142 } else {
5143 newfd = fcntl(from, F_DUPFD, to);
5144 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005145 if (newfd < 0) {
5146 if (errno == EMFILE)
5147 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005148 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005149 ash_msg_and_raise_error("%d: %m", from);
5150 }
5151 return newfd;
5152}
5153
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005154/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005155struct two_fd_t {
5156 int orig, copy;
5157};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005158struct redirtab {
5159 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005160 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005161 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005162 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005163};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005164#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005165
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005166static int need_to_remember(struct redirtab *rp, int fd)
5167{
5168 int i;
5169
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005170 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005171 return 0;
5172
5173 for (i = 0; i < rp->pair_count; i++) {
5174 if (rp->two_fd[i].orig == fd) {
5175 /* already remembered */
5176 return 0;
5177 }
5178 }
5179 return 1;
5180}
5181
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005182/* "hidden" fd is a fd used to read scripts, or a copy of such */
5183static int is_hidden_fd(struct redirtab *rp, int fd)
5184{
5185 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005186 struct parsefile *pf;
5187
5188 if (fd == -1)
5189 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005190 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005191 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005192 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005193 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005194 * $ ash # running ash interactively
5195 * $ . ./script.sh
5196 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005197 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005198 * it's still ok to use it: "read" builtin uses it,
5199 * why should we cripple "exec" builtin?
5200 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005201 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005202 return 1;
5203 }
5204 pf = pf->prev;
5205 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005206
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005207 if (!rp)
5208 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005209 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005210 fd |= COPYFD_RESTORE;
5211 for (i = 0; i < rp->pair_count; i++) {
5212 if (rp->two_fd[i].copy == fd) {
5213 return 1;
5214 }
5215 }
5216 return 0;
5217}
5218
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005219/*
5220 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5221 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005222 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005223 */
5224/* flags passed to redirect */
5225#define REDIR_PUSH 01 /* save previous values of file descriptors */
5226#define REDIR_SAVEFD2 03 /* set preverrout */
5227static void
5228redirect(union node *redir, int flags)
5229{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005230 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005231 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005232 int i;
5233 int fd;
5234 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005235 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005236
Denis Vlasenko01631112007-12-16 17:20:38 +00005237 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005238 if (!redir) {
5239 return;
5240 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005241
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005242 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005243 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005244 INT_OFF;
5245 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005246 union node *tmp = redir;
5247 do {
5248 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005249#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005250 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005251 sv_pos++;
5252#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005253 tmp = tmp->nfile.next;
5254 } while (tmp);
5255 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005256 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005257 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005258 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005259 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005260 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005261 while (sv_pos > 0) {
5262 sv_pos--;
5263 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5264 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005265 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005266
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005267 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005268 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005269 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005270 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005271 right_fd = redir->ndup.dupfd;
5272 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005273 /* redirect from/to same file descriptor? */
5274 if (right_fd == fd)
5275 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005276 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005277 if (is_hidden_fd(sv, right_fd)) {
5278 errno = EBADF; /* as if it is closed */
5279 ash_msg_and_raise_error("%d: %m", right_fd);
5280 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005281 newfd = -1;
5282 } else {
5283 newfd = openredirect(redir); /* always >= 0 */
5284 if (fd == newfd) {
5285 /* Descriptor wasn't open before redirect.
5286 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005287 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005288 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005289 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005290 continue;
5291 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005292 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005293#if ENABLE_ASH_BASH_COMPAT
5294 redirect_more:
5295#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005296 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005297 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005298 /* Careful to not accidentally "save"
5299 * to the same fd as right side fd in N>&M */
5300 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5301 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005302/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5303 * are closed in popredir() in the child, preventing them from leaking
5304 * into child. (popredir() also cleans up the mess in case of failures)
5305 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005306 if (i == -1) {
5307 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005308 if (i != EBADF) {
5309 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005310 if (newfd >= 0)
5311 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005312 errno = i;
5313 ash_msg_and_raise_error("%d: %m", fd);
5314 /* NOTREACHED */
5315 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005316 /* EBADF: it is not open - good, remember to close it */
5317 remember_to_close:
5318 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005319 } else { /* fd is open, save its copy */
5320 /* "exec fd>&-" should not close fds
5321 * which point to script file(s).
5322 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005323 if (is_hidden_fd(sv, fd))
5324 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005325 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005326 if (fd == 2)
5327 copied_fd2 = i;
5328 sv->two_fd[sv_pos].orig = fd;
5329 sv->two_fd[sv_pos].copy = i;
5330 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005331 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005332 if (newfd < 0) {
5333 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005334 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005335 /* Don't want to trigger debugging */
5336 if (fd != -1)
5337 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005338 } else {
5339 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005340 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005341 } else if (fd != newfd) { /* move newfd to fd */
5342 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005343#if ENABLE_ASH_BASH_COMPAT
5344 if (!(redir->nfile.type == NTO2 && fd == 2))
5345#endif
5346 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005347 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005348#if ENABLE_ASH_BASH_COMPAT
5349 if (redir->nfile.type == NTO2 && fd == 1) {
5350 /* We already redirected it to fd 1, now copy it to 2 */
5351 newfd = 1;
5352 fd = 2;
5353 goto redirect_more;
5354 }
5355#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005356 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005357
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005358 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005359 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5360 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005361}
5362
5363/*
5364 * Undo the effects of the last redirection.
5365 */
5366static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005367popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005368{
5369 struct redirtab *rp;
5370 int i;
5371
Denis Vlasenko01631112007-12-16 17:20:38 +00005372 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005373 return;
5374 INT_OFF;
5375 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005376 for (i = 0; i < rp->pair_count; i++) {
5377 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005378 int copy = rp->two_fd[i].copy;
5379 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005380 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005381 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005382 continue;
5383 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005384 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005385 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005386 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005387 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005388 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005389 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005390 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005391 }
5392 }
5393 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005394 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005395 free(rp);
5396 INT_ON;
5397}
5398
5399/*
5400 * Undo all redirections. Called on error or interrupt.
5401 */
5402
5403/*
5404 * Discard all saved file descriptors.
5405 */
5406static void
5407clearredir(int drop)
5408{
5409 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005410 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005411 if (!redirlist)
5412 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005413 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005414 }
5415}
5416
5417static int
5418redirectsafe(union node *redir, int flags)
5419{
5420 int err;
5421 volatile int saveint;
5422 struct jmploc *volatile savehandler = exception_handler;
5423 struct jmploc jmploc;
5424
5425 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005426 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5427 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005428 if (!err) {
5429 exception_handler = &jmploc;
5430 redirect(redir, flags);
5431 }
5432 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005433 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005434 longjmp(exception_handler->loc, 1);
5435 RESTORE_INT(saveint);
5436 return err;
5437}
5438
5439
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005440/* ============ Routines to expand arguments to commands
5441 *
5442 * We have to deal with backquotes, shell variables, and file metacharacters.
5443 */
5444
Mike Frysinger98c52642009-04-02 10:02:37 +00005445#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005446static arith_t
5447ash_arith(const char *s)
5448{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005449 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005450 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005451
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005452 math_state.lookupvar = lookupvar;
5453 math_state.setvar = setvar2;
5454 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005455
5456 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005457 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005458 if (math_state.errmsg)
5459 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005460 INT_ON;
5461
5462 return result;
5463}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005464#endif
5465
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005466/*
5467 * expandarg flags
5468 */
5469#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5470#define EXP_TILDE 0x2 /* do normal tilde expansion */
5471#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5472#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5473#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5474#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5475#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5476#define EXP_WORD 0x80 /* expand word in parameter expansion */
5477#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5478/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005479 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005480 */
5481#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5482#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5483#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5484#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5485#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5486
5487/*
5488 * Structure specifying which parts of the string should be searched
5489 * for IFS characters.
5490 */
5491struct ifsregion {
5492 struct ifsregion *next; /* next region in list */
5493 int begoff; /* offset of start of region */
5494 int endoff; /* offset of end of region */
5495 int nulonly; /* search for nul bytes only */
5496};
5497
5498struct arglist {
5499 struct strlist *list;
5500 struct strlist **lastp;
5501};
5502
5503/* output of current string */
5504static char *expdest;
5505/* list of back quote expressions */
5506static struct nodelist *argbackq;
5507/* first struct in list of ifs regions */
5508static struct ifsregion ifsfirst;
5509/* last struct in list */
5510static struct ifsregion *ifslastp;
5511/* holds expanded arg list */
5512static struct arglist exparg;
5513
5514/*
5515 * Our own itoa().
5516 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005517#if !ENABLE_SH_MATH_SUPPORT
5518/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5519typedef long arith_t;
5520# define ARITH_FMT "%ld"
5521#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005522static int
5523cvtnum(arith_t num)
5524{
5525 int len;
5526
5527 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005528 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005529 STADJUST(len, expdest);
5530 return len;
5531}
5532
5533static size_t
5534esclen(const char *start, const char *p)
5535{
5536 size_t esc = 0;
5537
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005538 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005539 esc++;
5540 }
5541 return esc;
5542}
5543
5544/*
5545 * Remove any CTLESC characters from a string.
5546 */
5547static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005548rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005549{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005550 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005551
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005552 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005553 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005554 unsigned protect_against_glob;
5555 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005556
5557 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005558 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005559 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005560
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005561 q = p;
5562 r = str;
5563 if (flag & RMESCAPE_ALLOC) {
5564 size_t len = p - str;
5565 size_t fulllen = len + strlen(p) + 1;
5566
5567 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005568 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005570 /* p and str may be invalidated by makestrspace */
5571 str = (char *)stackblock() + strloc;
5572 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005573 } else if (flag & RMESCAPE_HEAP) {
5574 r = ckmalloc(fulllen);
5575 } else {
5576 r = stalloc(fulllen);
5577 }
5578 q = r;
5579 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005580 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005581 }
5582 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005583
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005584 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5585 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005586 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005587 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005588 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005589// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5590// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5591// Note: both inquotes and protect_against_glob only affect whether
5592// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005593 inquotes = ~inquotes;
5594 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005595 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596 continue;
5597 }
5598 if (*p == '\\') {
5599 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005600 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005601 goto copy;
5602 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005603 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005604 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005605 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005606 *q++ = '\\';
5607 }
5608 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005609 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005610 copy:
5611 *q++ = *p++;
5612 }
5613 *q = '\0';
5614 if (flag & RMESCAPE_GROW) {
5615 expdest = r;
5616 STADJUST(q - r + 1, expdest);
5617 }
5618 return r;
5619}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005620#define pmatch(a, b) !fnmatch((a), (b), 0)
5621
5622/*
5623 * Prepare a pattern for a expmeta (internal glob(3)) call.
5624 *
5625 * Returns an stalloced string.
5626 */
5627static char *
5628preglob(const char *pattern, int quoted, int flag)
5629{
5630 flag |= RMESCAPE_GLOB;
5631 if (quoted) {
5632 flag |= RMESCAPE_QUOTED;
5633 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005634 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005635}
5636
5637/*
5638 * Put a string on the stack.
5639 */
5640static void
5641memtodest(const char *p, size_t len, int syntax, int quotes)
5642{
5643 char *q = expdest;
5644
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005645 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005646
5647 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005648 unsigned char c = *p++;
5649 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005650 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005651 if (quotes) {
5652 int n = SIT(c, syntax);
5653 if (n == CCTL || n == CBACK)
5654 USTPUTC(CTLESC, q);
5655 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005656 USTPUTC(c, q);
5657 }
5658
5659 expdest = q;
5660}
5661
5662static void
5663strtodest(const char *p, int syntax, int quotes)
5664{
5665 memtodest(p, strlen(p), syntax, quotes);
5666}
5667
5668/*
5669 * Record the fact that we have to scan this region of the
5670 * string for IFS characters.
5671 */
5672static void
5673recordregion(int start, int end, int nulonly)
5674{
5675 struct ifsregion *ifsp;
5676
5677 if (ifslastp == NULL) {
5678 ifsp = &ifsfirst;
5679 } else {
5680 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005681 ifsp = ckzalloc(sizeof(*ifsp));
5682 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005683 ifslastp->next = ifsp;
5684 INT_ON;
5685 }
5686 ifslastp = ifsp;
5687 ifslastp->begoff = start;
5688 ifslastp->endoff = end;
5689 ifslastp->nulonly = nulonly;
5690}
5691
5692static void
5693removerecordregions(int endoff)
5694{
5695 if (ifslastp == NULL)
5696 return;
5697
5698 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005699 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005700 struct ifsregion *ifsp;
5701 INT_OFF;
5702 ifsp = ifsfirst.next->next;
5703 free(ifsfirst.next);
5704 ifsfirst.next = ifsp;
5705 INT_ON;
5706 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005707 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005708 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005709 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005710 ifslastp = &ifsfirst;
5711 ifsfirst.endoff = endoff;
5712 }
5713 return;
5714 }
5715
5716 ifslastp = &ifsfirst;
5717 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005718 ifslastp = ifslastp->next;
5719 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005720 struct ifsregion *ifsp;
5721 INT_OFF;
5722 ifsp = ifslastp->next->next;
5723 free(ifslastp->next);
5724 ifslastp->next = ifsp;
5725 INT_ON;
5726 }
5727 if (ifslastp->endoff > endoff)
5728 ifslastp->endoff = endoff;
5729}
5730
5731static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005732exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005733{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005734 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005735 char *name;
5736 struct passwd *pw;
5737 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005738 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005739 int startloc;
5740
5741 name = p + 1;
5742
5743 while ((c = *++p) != '\0') {
5744 switch (c) {
5745 case CTLESC:
5746 return startp;
5747 case CTLQUOTEMARK:
5748 return startp;
5749 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005750 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005751 goto done;
5752 break;
5753 case '/':
5754 case CTLENDVAR:
5755 goto done;
5756 }
5757 }
5758 done:
5759 *p = '\0';
5760 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005761 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005762 } else {
5763 pw = getpwnam(name);
5764 if (pw == NULL)
5765 goto lose;
5766 home = pw->pw_dir;
5767 }
5768 if (!home || !*home)
5769 goto lose;
5770 *p = c;
5771 startloc = expdest - (char *)stackblock();
5772 strtodest(home, SQSYNTAX, quotes);
5773 recordregion(startloc, expdest - (char *)stackblock(), 0);
5774 return p;
5775 lose:
5776 *p = c;
5777 return startp;
5778}
5779
5780/*
5781 * Execute a command inside back quotes. If it's a builtin command, we
5782 * want to save its output in a block obtained from malloc. Otherwise
5783 * we fork off a subprocess and get the output of the command via a pipe.
5784 * Should be called with interrupts off.
5785 */
5786struct backcmd { /* result of evalbackcmd */
5787 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005788 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005789 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005790 struct job *jp; /* job structure for command */
5791};
5792
5793/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005794static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005795#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005796static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005797
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005798static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005799evalbackcmd(union node *n, struct backcmd *result)
5800{
5801 int saveherefd;
5802
5803 result->fd = -1;
5804 result->buf = NULL;
5805 result->nleft = 0;
5806 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005807 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005808 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005809
5810 saveherefd = herefd;
5811 herefd = -1;
5812
5813 {
5814 int pip[2];
5815 struct job *jp;
5816
5817 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005818 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005819 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005820 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5821 FORCE_INT_ON;
5822 close(pip[0]);
5823 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005824 /*close(1);*/
5825 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005826 close(pip[1]);
5827 }
5828 eflag = 0;
5829 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5830 /* NOTREACHED */
5831 }
5832 close(pip[1]);
5833 result->fd = pip[0];
5834 result->jp = jp;
5835 }
5836 herefd = saveherefd;
5837 out:
5838 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5839 result->fd, result->buf, result->nleft, result->jp));
5840}
5841
5842/*
5843 * Expand stuff in backwards quotes.
5844 */
5845static void
5846expbackq(union node *cmd, int quoted, int quotes)
5847{
5848 struct backcmd in;
5849 int i;
5850 char buf[128];
5851 char *p;
5852 char *dest;
5853 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005854 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005855 struct stackmark smark;
5856
5857 INT_OFF;
5858 setstackmark(&smark);
5859 dest = expdest;
5860 startloc = dest - (char *)stackblock();
5861 grabstackstr(dest);
5862 evalbackcmd(cmd, &in);
5863 popstackmark(&smark);
5864
5865 p = in.buf;
5866 i = in.nleft;
5867 if (i == 0)
5868 goto read;
5869 for (;;) {
5870 memtodest(p, i, syntax, quotes);
5871 read:
5872 if (in.fd < 0)
5873 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005874 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005875 TRACE(("expbackq: read returns %d\n", i));
5876 if (i <= 0)
5877 break;
5878 p = buf;
5879 }
5880
Denis Vlasenko60818682007-09-28 22:07:23 +00005881 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005882 if (in.fd >= 0) {
5883 close(in.fd);
5884 back_exitstatus = waitforjob(in.jp);
5885 }
5886 INT_ON;
5887
5888 /* Eat all trailing newlines */
5889 dest = expdest;
5890 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5891 STUNPUTC(dest);
5892 expdest = dest;
5893
5894 if (quoted == 0)
5895 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005896 TRACE(("evalbackq: size:%d:'%.*s'\n",
5897 (int)((dest - (char *)stackblock()) - startloc),
5898 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005899 stackblock() + startloc));
5900}
5901
Mike Frysinger98c52642009-04-02 10:02:37 +00005902#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005903/*
5904 * Expand arithmetic expression. Backup to start of expression,
5905 * evaluate, place result in (backed up) result, adjust string position.
5906 */
5907static void
5908expari(int quotes)
5909{
5910 char *p, *start;
5911 int begoff;
5912 int flag;
5913 int len;
5914
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005915 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005916
5917 /*
5918 * This routine is slightly over-complicated for
5919 * efficiency. Next we scan backwards looking for the
5920 * start of arithmetic.
5921 */
5922 start = stackblock();
5923 p = expdest - 1;
5924 *p = '\0';
5925 p--;
5926 do {
5927 int esc;
5928
Denys Vlasenkocd716832009-11-28 22:14:02 +01005929 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005930 p--;
5931#if DEBUG
5932 if (p < start) {
5933 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5934 }
5935#endif
5936 }
5937
5938 esc = esclen(start, p);
5939 if (!(esc % 2)) {
5940 break;
5941 }
5942
5943 p -= esc + 1;
5944 } while (1);
5945
5946 begoff = p - start;
5947
5948 removerecordregions(begoff);
5949
5950 flag = p[1];
5951
5952 expdest = p;
5953
5954 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005955 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005956
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005957 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958
5959 if (flag != '"')
5960 recordregion(begoff, begoff + len, 0);
5961}
5962#endif
5963
5964/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005965static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005966
5967/*
5968 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5969 * characters to allow for further processing. Otherwise treat
5970 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005971 *
5972 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5973 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5974 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005975 */
5976static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005977argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005978{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005979 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005980 '=',
5981 ':',
5982 CTLQUOTEMARK,
5983 CTLENDVAR,
5984 CTLESC,
5985 CTLVAR,
5986 CTLBACKQ,
5987 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00005988#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005989 CTLENDARI,
5990#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01005991 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005992 };
5993 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005994 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
5995 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996 int inquotes;
5997 size_t length;
5998 int startloc;
5999
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006000 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006001 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006002 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006003 reject++;
6004 }
6005 inquotes = 0;
6006 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006007 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006008 char *q;
6009
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006010 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006011 tilde:
6012 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006013 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006014 q++;
6015 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006016 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006017 }
6018 start:
6019 startloc = expdest - (char *)stackblock();
6020 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006021 unsigned char c;
6022
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006024 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006025 if (c) {
6026 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006027 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006028 ) {
6029 /* c == '=' || c == ':' || c == CTLENDARI */
6030 length++;
6031 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006032 }
6033 if (length > 0) {
6034 int newloc;
6035 expdest = stack_nputstr(p, length, expdest);
6036 newloc = expdest - (char *)stackblock();
6037 if (breakall && !inquotes && newloc > startloc) {
6038 recordregion(startloc, newloc, 0);
6039 }
6040 startloc = newloc;
6041 }
6042 p += length + 1;
6043 length = 0;
6044
6045 switch (c) {
6046 case '\0':
6047 goto breakloop;
6048 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006049 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006050 p--;
6051 continue;
6052 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006053 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006054 reject++;
6055 /* fall through */
6056 case ':':
6057 /*
6058 * sort of a hack - expand tildes in variable
6059 * assignments (after the first '=' and after ':'s).
6060 */
6061 if (*--p == '~') {
6062 goto tilde;
6063 }
6064 continue;
6065 }
6066
6067 switch (c) {
6068 case CTLENDVAR: /* ??? */
6069 goto breakloop;
6070 case CTLQUOTEMARK:
6071 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006072 if (!inquotes
6073 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006074 && ( p[4] == (char)CTLQUOTEMARK
6075 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006076 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006077 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006078 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006079 goto start;
6080 }
6081 inquotes = !inquotes;
6082 addquote:
6083 if (quotes) {
6084 p--;
6085 length++;
6086 startloc++;
6087 }
6088 break;
6089 case CTLESC:
6090 startloc++;
6091 length++;
6092 goto addquote;
6093 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006094 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006095 goto start;
6096 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006097 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006098 case CTLBACKQ|CTLQUOTE:
6099 expbackq(argbackq->n, c, quotes);
6100 argbackq = argbackq->next;
6101 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006102#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006103 case CTLENDARI:
6104 p--;
6105 expari(quotes);
6106 goto start;
6107#endif
6108 }
6109 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006110 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006111}
6112
6113static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006114scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6115 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006116{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006117 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006118 char c;
6119
6120 loc = startp;
6121 loc2 = rmesc;
6122 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006123 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006124 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006125
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006126 c = *loc2;
6127 if (zero) {
6128 *loc2 = '\0';
6129 s = rmesc;
6130 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006131 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006132
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006133 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006134 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006135 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006136 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006137 loc++;
6138 loc++;
6139 loc2++;
6140 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006141 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006142}
6143
6144static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006145scanright(char *startp, char *rmesc, char *rmescend,
6146 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006147{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006148#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6149 int try2optimize = match_at_start;
6150#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006151 int esc = 0;
6152 char *loc;
6153 char *loc2;
6154
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006155 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6156 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6157 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6158 * Logic:
6159 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6160 * and on each iteration they go back two/one char until they reach the beginning.
6161 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6162 */
6163 /* TODO: document in what other circumstances we are called. */
6164
6165 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166 int match;
6167 char c = *loc2;
6168 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006169 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006170 *loc2 = '\0';
6171 s = rmesc;
6172 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006173 match = pmatch(pattern, s);
6174 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006175 *loc2 = c;
6176 if (match)
6177 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006178#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6179 if (try2optimize) {
6180 /* Maybe we can optimize this:
6181 * if pattern ends with unescaped *, we can avoid checking
6182 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6183 * it wont match truncated "raw_value_of_" strings too.
6184 */
6185 unsigned plen = strlen(pattern);
6186 /* Does it end with "*"? */
6187 if (plen != 0 && pattern[--plen] == '*') {
6188 /* "xxxx*" is not escaped */
6189 /* "xxx\*" is escaped */
6190 /* "xx\\*" is not escaped */
6191 /* "x\\\*" is escaped */
6192 int slashes = 0;
6193 while (plen != 0 && pattern[--plen] == '\\')
6194 slashes++;
6195 if (!(slashes & 1))
6196 break; /* ends with unescaped "*" */
6197 }
6198 try2optimize = 0;
6199 }
6200#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006201 loc--;
6202 if (quotes) {
6203 if (--esc < 0) {
6204 esc = esclen(startp, loc);
6205 }
6206 if (esc % 2) {
6207 esc--;
6208 loc--;
6209 }
6210 }
6211 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006212 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006213}
6214
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006215static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006216static void
6217varunset(const char *end, const char *var, const char *umsg, int varflags)
6218{
6219 const char *msg;
6220 const char *tail;
6221
6222 tail = nullstr;
6223 msg = "parameter not set";
6224 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006225 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006226 if (varflags & VSNUL)
6227 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006228 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006229 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006230 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006231 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006232 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006233}
6234
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006235#if ENABLE_ASH_BASH_COMPAT
6236static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006237parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006238{
6239 char *idx, *repl = NULL;
6240 unsigned char c;
6241
Denys Vlasenko16149002010-08-06 22:06:21 +02006242 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006243 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006244 idx = arg;
6245 while (1) {
6246 c = *arg;
6247 if (!c)
6248 break;
6249 if (c == '/') {
6250 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006251 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006252 repl = idx + 1;
6253 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006254 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006255 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006256 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006257 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006258 /*
6259 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6260 * The result is a_\_z_c (not a\_\_z_c)!
6261 *
6262 * Enable debug prints in this function and you'll see:
6263 * ash: arg:'\\b/_\\_z_' varflags:d
6264 * ash: pattern:'\\b' repl:'_\_z_'
6265 * That is, \\b is interpreted as \\b, but \\_ as \_!
6266 * IOW: search pattern and replace string treat backslashes
6267 * differently! That is the reason why we check repl below:
6268 */
6269 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6270 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006271 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006272 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006273 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006274
6275 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006276}
6277#endif /* ENABLE_ASH_BASH_COMPAT */
6278
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006279static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006280subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006281 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006282{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006283 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006284 char *startp;
6285 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006286 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006287 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006288 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006289 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006290 int saveherefd = herefd;
6291 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006292 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006293 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006294
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006295 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6296 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006297
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006298 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006299 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6300 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006301 STPUTC('\0', expdest);
6302 herefd = saveherefd;
6303 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006304 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006305
6306 switch (subtype) {
6307 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006308 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006309 amount = startp - expdest;
6310 STADJUST(amount, expdest);
6311 return startp;
6312
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006313 case VSQUESTION:
6314 varunset(p, varname, startp, varflags);
6315 /* NOTREACHED */
6316
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006317#if ENABLE_ASH_BASH_COMPAT
6318 case VSSUBSTR:
6319 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006320 /* Read POS in ${var:POS:LEN} */
6321 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006322 len = str - startp - 1;
6323
6324 /* *loc != '\0', guaranteed by parser */
6325 if (quotes) {
6326 char *ptr;
6327
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006328 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006329 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006330 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006331 len--;
6332 ptr++;
6333 }
6334 }
6335 }
6336 orig_len = len;
6337
6338 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006339 /* ${var::LEN} */
6340 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006341 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006342 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006343 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006344 while (*loc && *loc != ':') {
6345 /* TODO?
6346 * bash complains on: var=qwe; echo ${var:1a:123}
6347 if (!isdigit(*loc))
6348 ash_msg_and_raise_error(msg_illnum, str);
6349 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006350 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006351 }
6352 if (*loc++ == ':') {
6353 len = number(loc);
6354 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006355 }
6356 if (pos >= orig_len) {
6357 pos = 0;
6358 len = 0;
6359 }
6360 if (len > (orig_len - pos))
6361 len = orig_len - pos;
6362
6363 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006364 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006365 str++;
6366 }
6367 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006368 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006369 *loc++ = *str++;
6370 *loc++ = *str++;
6371 }
6372 *loc = '\0';
6373 amount = loc - expdest;
6374 STADJUST(amount, expdest);
6375 return loc;
6376#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006377 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006378
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006379 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006380
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006381 /* We'll comeback here if we grow the stack while handling
6382 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6383 * stack will need rebasing, and we'll need to remove our work
6384 * areas each time
6385 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006386 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006387
6388 amount = expdest - ((char *)stackblock() + resetloc);
6389 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006390 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006391
6392 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006393 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006394 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006395 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006396 if (rmesc != startp) {
6397 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006398 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006399 }
6400 }
6401 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006402 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006403 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006404 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006405
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006406#if ENABLE_ASH_BASH_COMPAT
6407 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006408 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006409
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006410 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006411 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006412 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006413 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006414 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006415 }
6416
6417 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006418 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006419 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006420
6421 len = 0;
6422 idx = startp;
6423 end = str - 1;
6424 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006425 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006426 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006427 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006428 if (!loc) {
6429 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006430 char *restart_detect = stackblock();
6431 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006432 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006433 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006434 idx++;
6435 len++;
6436 STPUTC(*idx, expdest);
6437 }
6438 if (stackblock() != restart_detect)
6439 goto restart;
6440 idx++;
6441 len++;
6442 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006443 /* continue; - prone to quadratic behavior, smarter code: */
6444 if (idx >= end)
6445 break;
6446 if (str[0] == '*') {
6447 /* Pattern is "*foo". If "*foo" does not match "long_string",
6448 * it would never match "ong_string" etc, no point in trying.
6449 */
6450 goto skip_matching;
6451 }
6452 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006453 }
6454
6455 if (subtype == VSREPLACEALL) {
6456 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006457 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006458 idx++;
6459 idx++;
6460 rmesc++;
6461 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006462 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006463 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006464 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006465
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006466 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006467 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006468 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006469 if (quotes && *loc == '\\') {
6470 STPUTC(CTLESC, expdest);
6471 len++;
6472 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006473 STPUTC(*loc, expdest);
6474 if (stackblock() != restart_detect)
6475 goto restart;
6476 len++;
6477 }
6478
6479 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006480 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006481 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006482 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006483 STPUTC(*idx, expdest);
6484 if (stackblock() != restart_detect)
6485 goto restart;
6486 len++;
6487 idx++;
6488 }
6489 break;
6490 }
6491 }
6492
6493 /* We've put the replaced text into a buffer at workloc, now
6494 * move it to the right place and adjust the stack.
6495 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006496 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006497 startp = (char *)stackblock() + startloc;
6498 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006499 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006500 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006501 STADJUST(-amount, expdest);
6502 return startp;
6503 }
6504#endif /* ENABLE_ASH_BASH_COMPAT */
6505
6506 subtype -= VSTRIMRIGHT;
6507#if DEBUG
6508 if (subtype < 0 || subtype > 7)
6509 abort();
6510#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006511 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006512 zero = subtype >> 1;
6513 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6514 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6515
6516 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6517 if (loc) {
6518 if (zero) {
6519 memmove(startp, loc, str - loc);
6520 loc = startp + (str - loc) - 1;
6521 }
6522 *loc = '\0';
6523 amount = loc - expdest;
6524 STADJUST(amount, expdest);
6525 }
6526 return loc;
6527}
6528
6529/*
6530 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006531 * name parameter (examples):
6532 * ash -c 'echo $1' name:'1='
6533 * ash -c 'echo $qwe' name:'qwe='
6534 * ash -c 'echo $$' name:'$='
6535 * ash -c 'echo ${$}' name:'$='
6536 * ash -c 'echo ${$##q}' name:'$=q'
6537 * ash -c 'echo ${#$}' name:'$='
6538 * note: examples with bad shell syntax:
6539 * ash -c 'echo ${#$1}' name:'$=1'
6540 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006541 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006542static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006543varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544{
Mike Frysinger98c52642009-04-02 10:02:37 +00006545 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006546 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006547 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006548 int sepq = 0;
6549 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006550 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006551 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006552 int quoted = varflags & VSQUOTE;
6553 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006554
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006555 switch (*name) {
6556 case '$':
6557 num = rootpid;
6558 goto numvar;
6559 case '?':
6560 num = exitstatus;
6561 goto numvar;
6562 case '#':
6563 num = shellparam.nparam;
6564 goto numvar;
6565 case '!':
6566 num = backgndpid;
6567 if (num == 0)
6568 return -1;
6569 numvar:
6570 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006571 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006572 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006573 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006574 for (i = NOPTS - 1; i >= 0; i--) {
6575 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006576 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006577 len++;
6578 }
6579 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006580 check_1char_name:
6581#if 0
6582 /* handles cases similar to ${#$1} */
6583 if (name[2] != '\0')
6584 raise_error_syntax("bad substitution");
6585#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006586 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006587 case '@': {
6588 char **ap;
6589 int sep;
6590
6591 if (quoted && (flags & EXP_FULL)) {
6592 /* note: this is not meant as PEOF value */
6593 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006595 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006596 /* fall through */
6597 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006598 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006599 i = SIT(sep, syntax);
6600 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006601 sepq = 1;
6602 param:
6603 ap = shellparam.p;
6604 if (!ap)
6605 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006606 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006607 size_t partlen;
6608
6609 partlen = strlen(p);
6610 len += partlen;
6611
6612 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6613 memtodest(p, partlen, syntax, quotes);
6614
6615 if (*ap && sep) {
6616 char *q;
6617
6618 len++;
6619 if (subtype == VSPLUS || subtype == VSLENGTH) {
6620 continue;
6621 }
6622 q = expdest;
6623 if (sepq)
6624 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006625 /* note: may put NUL despite sep != 0
6626 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006627 STPUTC(sep, q);
6628 expdest = q;
6629 }
6630 }
6631 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006632 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006633 case '0':
6634 case '1':
6635 case '2':
6636 case '3':
6637 case '4':
6638 case '5':
6639 case '6':
6640 case '7':
6641 case '8':
6642 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006643 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006644 if (num < 0 || num > shellparam.nparam)
6645 return -1;
6646 p = num ? shellparam.p[num - 1] : arg0;
6647 goto value;
6648 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006649 /* NB: name has form "VAR=..." */
6650
6651 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6652 * which should be considered before we check variables. */
6653 if (var_str_list) {
6654 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6655 p = NULL;
6656 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006657 char *str, *eq;
6658 str = var_str_list->text;
6659 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006660 if (!eq) /* stop at first non-assignment */
6661 break;
6662 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006663 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006664 && strncmp(str, name, name_len) == 0
6665 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006666 p = eq;
6667 /* goto value; - WRONG! */
6668 /* think "A=1 A=2 B=$A" */
6669 }
6670 var_str_list = var_str_list->next;
6671 } while (var_str_list);
6672 if (p)
6673 goto value;
6674 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006675 p = lookupvar(name);
6676 value:
6677 if (!p)
6678 return -1;
6679
6680 len = strlen(p);
6681 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6682 memtodest(p, len, syntax, quotes);
6683 return len;
6684 }
6685
6686 if (subtype == VSPLUS || subtype == VSLENGTH)
6687 STADJUST(-len, expdest);
6688 return len;
6689}
6690
6691/*
6692 * Expand a variable, and return a pointer to the next character in the
6693 * input string.
6694 */
6695static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006696evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006697{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006698 char varflags;
6699 char subtype;
6700 char quoted;
6701 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006702 char *var;
6703 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006704 int startloc;
6705 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006706
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006707 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006708 subtype = varflags & VSTYPE;
6709 quoted = varflags & VSQUOTE;
6710 var = p;
6711 easy = (!quoted || (*var == '@' && shellparam.nparam));
6712 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006713 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006714
6715 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006716 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006717 if (varflags & VSNUL)
6718 varlen--;
6719
6720 if (subtype == VSPLUS) {
6721 varlen = -1 - varlen;
6722 goto vsplus;
6723 }
6724
6725 if (subtype == VSMINUS) {
6726 vsplus:
6727 if (varlen < 0) {
6728 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006729 p,
6730 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006731 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006732 );
6733 goto end;
6734 }
6735 if (easy)
6736 goto record;
6737 goto end;
6738 }
6739
6740 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6741 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006742 if (subevalvar(p, var, /* strloc: */ 0,
6743 subtype, startloc, varflags,
6744 /* quotes: */ 0,
6745 var_str_list)
6746 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006747 varflags &= ~VSNUL;
6748 /*
6749 * Remove any recorded regions beyond
6750 * start of variable
6751 */
6752 removerecordregions(startloc);
6753 goto again;
6754 }
6755 goto end;
6756 }
6757 if (easy)
6758 goto record;
6759 goto end;
6760 }
6761
6762 if (varlen < 0 && uflag)
6763 varunset(p, var, 0, 0);
6764
6765 if (subtype == VSLENGTH) {
6766 cvtnum(varlen > 0 ? varlen : 0);
6767 goto record;
6768 }
6769
6770 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006771 if (easy)
6772 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006773 goto end;
6774 }
6775
6776#if DEBUG
6777 switch (subtype) {
6778 case VSTRIMLEFT:
6779 case VSTRIMLEFTMAX:
6780 case VSTRIMRIGHT:
6781 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006782#if ENABLE_ASH_BASH_COMPAT
6783 case VSSUBSTR:
6784 case VSREPLACE:
6785 case VSREPLACEALL:
6786#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006787 break;
6788 default:
6789 abort();
6790 }
6791#endif
6792
6793 if (varlen >= 0) {
6794 /*
6795 * Terminate the string and start recording the pattern
6796 * right after it
6797 */
6798 STPUTC('\0', expdest);
6799 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006800 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006801 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006802//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006803 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006804 var_str_list)
6805 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006806 int amount = expdest - (
6807 (char *)stackblock() + patloc - 1
6808 );
6809 STADJUST(-amount, expdest);
6810 }
6811 /* Remove any recorded regions beyond start of variable */
6812 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006813 record:
6814 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006815 }
6816
6817 end:
6818 if (subtype != VSNORMAL) { /* skip to end of alternative */
6819 int nesting = 1;
6820 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006821 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006822 if (c == CTLESC)
6823 p++;
6824 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6825 if (varlen >= 0)
6826 argbackq = argbackq->next;
6827 } else if (c == CTLVAR) {
6828 if ((*p++ & VSTYPE) != VSNORMAL)
6829 nesting++;
6830 } else if (c == CTLENDVAR) {
6831 if (--nesting == 0)
6832 break;
6833 }
6834 }
6835 }
6836 return p;
6837}
6838
6839/*
6840 * Break the argument string into pieces based upon IFS and add the
6841 * strings to the argument list. The regions of the string to be
6842 * searched for IFS characters have been stored by recordregion.
6843 */
6844static void
6845ifsbreakup(char *string, struct arglist *arglist)
6846{
6847 struct ifsregion *ifsp;
6848 struct strlist *sp;
6849 char *start;
6850 char *p;
6851 char *q;
6852 const char *ifs, *realifs;
6853 int ifsspc;
6854 int nulonly;
6855
6856 start = string;
6857 if (ifslastp != NULL) {
6858 ifsspc = 0;
6859 nulonly = 0;
6860 realifs = ifsset() ? ifsval() : defifs;
6861 ifsp = &ifsfirst;
6862 do {
6863 p = string + ifsp->begoff;
6864 nulonly = ifsp->nulonly;
6865 ifs = nulonly ? nullstr : realifs;
6866 ifsspc = 0;
6867 while (p < string + ifsp->endoff) {
6868 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006869 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006870 p++;
6871 if (!strchr(ifs, *p)) {
6872 p++;
6873 continue;
6874 }
6875 if (!nulonly)
6876 ifsspc = (strchr(defifs, *p) != NULL);
6877 /* Ignore IFS whitespace at start */
6878 if (q == start && ifsspc) {
6879 p++;
6880 start = p;
6881 continue;
6882 }
6883 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006884 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006885 sp->text = start;
6886 *arglist->lastp = sp;
6887 arglist->lastp = &sp->next;
6888 p++;
6889 if (!nulonly) {
6890 for (;;) {
6891 if (p >= string + ifsp->endoff) {
6892 break;
6893 }
6894 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006895 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006896 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006897 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006898 p = q;
6899 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006900 }
6901 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006902 if (ifsspc) {
6903 p++;
6904 ifsspc = 0;
6905 } else {
6906 p = q;
6907 break;
6908 }
6909 } else
6910 p++;
6911 }
6912 }
6913 start = p;
6914 } /* while */
6915 ifsp = ifsp->next;
6916 } while (ifsp != NULL);
6917 if (nulonly)
6918 goto add;
6919 }
6920
6921 if (!*start)
6922 return;
6923
6924 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006925 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006926 sp->text = start;
6927 *arglist->lastp = sp;
6928 arglist->lastp = &sp->next;
6929}
6930
6931static void
6932ifsfree(void)
6933{
6934 struct ifsregion *p;
6935
6936 INT_OFF;
6937 p = ifsfirst.next;
6938 do {
6939 struct ifsregion *ifsp;
6940 ifsp = p->next;
6941 free(p);
6942 p = ifsp;
6943 } while (p);
6944 ifslastp = NULL;
6945 ifsfirst.next = NULL;
6946 INT_ON;
6947}
6948
6949/*
6950 * Add a file name to the list.
6951 */
6952static void
6953addfname(const char *name)
6954{
6955 struct strlist *sp;
6956
Denis Vlasenko597906c2008-02-20 16:38:54 +00006957 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006958 sp->text = ststrdup(name);
6959 *exparg.lastp = sp;
6960 exparg.lastp = &sp->next;
6961}
6962
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006963/*
6964 * Do metacharacter (i.e. *, ?, [...]) expansion.
6965 */
6966static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006967expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006968{
6969 char *p;
6970 const char *cp;
6971 char *start;
6972 char *endname;
6973 int metaflag;
6974 struct stat statb;
6975 DIR *dirp;
6976 struct dirent *dp;
6977 int atend;
6978 int matchdot;
6979
6980 metaflag = 0;
6981 start = name;
6982 for (p = name; *p; p++) {
6983 if (*p == '*' || *p == '?')
6984 metaflag = 1;
6985 else if (*p == '[') {
6986 char *q = p + 1;
6987 if (*q == '!')
6988 q++;
6989 for (;;) {
6990 if (*q == '\\')
6991 q++;
6992 if (*q == '/' || *q == '\0')
6993 break;
6994 if (*++q == ']') {
6995 metaflag = 1;
6996 break;
6997 }
6998 }
6999 } else if (*p == '\\')
7000 p++;
7001 else if (*p == '/') {
7002 if (metaflag)
7003 goto out;
7004 start = p + 1;
7005 }
7006 }
7007 out:
7008 if (metaflag == 0) { /* we've reached the end of the file name */
7009 if (enddir != expdir)
7010 metaflag++;
7011 p = name;
7012 do {
7013 if (*p == '\\')
7014 p++;
7015 *enddir++ = *p;
7016 } while (*p++);
7017 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7018 addfname(expdir);
7019 return;
7020 }
7021 endname = p;
7022 if (name < start) {
7023 p = name;
7024 do {
7025 if (*p == '\\')
7026 p++;
7027 *enddir++ = *p++;
7028 } while (p < start);
7029 }
7030 if (enddir == expdir) {
7031 cp = ".";
7032 } else if (enddir == expdir + 1 && *expdir == '/') {
7033 cp = "/";
7034 } else {
7035 cp = expdir;
7036 enddir[-1] = '\0';
7037 }
7038 dirp = opendir(cp);
7039 if (dirp == NULL)
7040 return;
7041 if (enddir != expdir)
7042 enddir[-1] = '/';
7043 if (*endname == 0) {
7044 atend = 1;
7045 } else {
7046 atend = 0;
7047 *endname++ = '\0';
7048 }
7049 matchdot = 0;
7050 p = start;
7051 if (*p == '\\')
7052 p++;
7053 if (*p == '.')
7054 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007055 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007056 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007057 continue;
7058 if (pmatch(start, dp->d_name)) {
7059 if (atend) {
7060 strcpy(enddir, dp->d_name);
7061 addfname(expdir);
7062 } else {
7063 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7064 continue;
7065 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007066 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007067 }
7068 }
7069 }
7070 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007071 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007072 endname[-1] = '/';
7073}
7074
7075static struct strlist *
7076msort(struct strlist *list, int len)
7077{
7078 struct strlist *p, *q = NULL;
7079 struct strlist **lpp;
7080 int half;
7081 int n;
7082
7083 if (len <= 1)
7084 return list;
7085 half = len >> 1;
7086 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007087 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007088 q = p;
7089 p = p->next;
7090 }
7091 q->next = NULL; /* terminate first half of list */
7092 q = msort(list, half); /* sort first half of list */
7093 p = msort(p, len - half); /* sort second half */
7094 lpp = &list;
7095 for (;;) {
7096#if ENABLE_LOCALE_SUPPORT
7097 if (strcoll(p->text, q->text) < 0)
7098#else
7099 if (strcmp(p->text, q->text) < 0)
7100#endif
7101 {
7102 *lpp = p;
7103 lpp = &p->next;
7104 p = *lpp;
7105 if (p == NULL) {
7106 *lpp = q;
7107 break;
7108 }
7109 } else {
7110 *lpp = q;
7111 lpp = &q->next;
7112 q = *lpp;
7113 if (q == NULL) {
7114 *lpp = p;
7115 break;
7116 }
7117 }
7118 }
7119 return list;
7120}
7121
7122/*
7123 * Sort the results of file name expansion. It calculates the number of
7124 * strings to sort and then calls msort (short for merge sort) to do the
7125 * work.
7126 */
7127static struct strlist *
7128expsort(struct strlist *str)
7129{
7130 int len;
7131 struct strlist *sp;
7132
7133 len = 0;
7134 for (sp = str; sp; sp = sp->next)
7135 len++;
7136 return msort(str, len);
7137}
7138
7139static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007140expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007141{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007142 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007143 '*', '?', '[', 0
7144 };
7145 /* TODO - EXP_REDIR */
7146
7147 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007148 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007149 struct strlist **savelastp;
7150 struct strlist *sp;
7151 char *p;
7152
7153 if (fflag)
7154 goto nometa;
7155 if (!strpbrk(str->text, metachars))
7156 goto nometa;
7157 savelastp = exparg.lastp;
7158
7159 INT_OFF;
7160 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7161 {
7162 int i = strlen(str->text);
7163 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7164 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007165 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007166 free(expdir);
7167 if (p != str->text)
7168 free(p);
7169 INT_ON;
7170 if (exparg.lastp == savelastp) {
7171 /*
7172 * no matches
7173 */
7174 nometa:
7175 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007176 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007177 exparg.lastp = &str->next;
7178 } else {
7179 *exparg.lastp = NULL;
7180 *savelastp = sp = expsort(*savelastp);
7181 while (sp->next != NULL)
7182 sp = sp->next;
7183 exparg.lastp = &sp->next;
7184 }
7185 str = str->next;
7186 }
7187}
7188
7189/*
7190 * Perform variable substitution and command substitution on an argument,
7191 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7192 * perform splitting and file name expansion. When arglist is NULL, perform
7193 * here document expansion.
7194 */
7195static void
7196expandarg(union node *arg, struct arglist *arglist, int flag)
7197{
7198 struct strlist *sp;
7199 char *p;
7200
7201 argbackq = arg->narg.backquote;
7202 STARTSTACKSTR(expdest);
7203 ifsfirst.next = NULL;
7204 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007205 argstr(arg->narg.text, flag,
7206 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007207 p = _STPUTC('\0', expdest);
7208 expdest = p - 1;
7209 if (arglist == NULL) {
7210 return; /* here document expanded */
7211 }
7212 p = grabstackstr(p);
7213 exparg.lastp = &exparg.list;
7214 /*
7215 * TODO - EXP_REDIR
7216 */
7217 if (flag & EXP_FULL) {
7218 ifsbreakup(p, &exparg);
7219 *exparg.lastp = NULL;
7220 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007221 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007222 } else {
7223 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007224 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007225 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007226 sp->text = p;
7227 *exparg.lastp = sp;
7228 exparg.lastp = &sp->next;
7229 }
7230 if (ifsfirst.next)
7231 ifsfree();
7232 *exparg.lastp = NULL;
7233 if (exparg.list) {
7234 *arglist->lastp = exparg.list;
7235 arglist->lastp = exparg.lastp;
7236 }
7237}
7238
7239/*
7240 * Expand shell variables and backquotes inside a here document.
7241 */
7242static void
7243expandhere(union node *arg, int fd)
7244{
7245 herefd = fd;
7246 expandarg(arg, (struct arglist *)NULL, 0);
7247 full_write(fd, stackblock(), expdest - (char *)stackblock());
7248}
7249
7250/*
7251 * Returns true if the pattern matches the string.
7252 */
7253static int
7254patmatch(char *pattern, const char *string)
7255{
7256 return pmatch(preglob(pattern, 0, 0), string);
7257}
7258
7259/*
7260 * See if a pattern matches in a case statement.
7261 */
7262static int
7263casematch(union node *pattern, char *val)
7264{
7265 struct stackmark smark;
7266 int result;
7267
7268 setstackmark(&smark);
7269 argbackq = pattern->narg.backquote;
7270 STARTSTACKSTR(expdest);
7271 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007272 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7273 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007274 STACKSTRNUL(expdest);
7275 result = patmatch(stackblock(), val);
7276 popstackmark(&smark);
7277 return result;
7278}
7279
7280
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007281/* ============ find_command */
7282
7283struct builtincmd {
7284 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007285 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007286 /* unsigned flags; */
7287};
7288#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007289/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007290 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007291#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007292#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007293
7294struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007295 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007296 union param {
7297 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007298 /* index >= 0 for commands without path (slashes) */
7299 /* (TODO: what exactly does the value mean? PATH position?) */
7300 /* index == -1 for commands with slashes */
7301 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007302 const struct builtincmd *cmd;
7303 struct funcnode *func;
7304 } u;
7305};
7306/* values of cmdtype */
7307#define CMDUNKNOWN -1 /* no entry in table for command */
7308#define CMDNORMAL 0 /* command is an executable program */
7309#define CMDFUNCTION 1 /* command is a shell function */
7310#define CMDBUILTIN 2 /* command is a shell builtin */
7311
7312/* action to find_command() */
7313#define DO_ERR 0x01 /* prints errors */
7314#define DO_ABS 0x02 /* checks absolute paths */
7315#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7316#define DO_ALTPATH 0x08 /* using alternate path */
7317#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7318
7319static void find_command(char *, struct cmdentry *, int, const char *);
7320
7321
7322/* ============ Hashing commands */
7323
7324/*
7325 * When commands are first encountered, they are entered in a hash table.
7326 * This ensures that a full path search will not have to be done for them
7327 * on each invocation.
7328 *
7329 * We should investigate converting to a linear search, even though that
7330 * would make the command name "hash" a misnomer.
7331 */
7332
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333struct tblentry {
7334 struct tblentry *next; /* next entry in hash chain */
7335 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007336 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007337 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007338 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007339};
7340
Denis Vlasenko01631112007-12-16 17:20:38 +00007341static struct tblentry **cmdtable;
7342#define INIT_G_cmdtable() do { \
7343 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7344} while (0)
7345
7346static int builtinloc = -1; /* index in path of %builtin, or -1 */
7347
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007348
7349static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007350tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007351{
7352 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007353
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007354#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007355 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007356 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007357 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007358 while (*envp)
7359 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007360 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007361 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007362 /* re-exec ourselves with the new arguments */
7363 execve(bb_busybox_exec_path, argv, envp);
7364 /* If they called chroot or otherwise made the binary no longer
7365 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007366 }
7367#endif
7368
7369 repeat:
7370#ifdef SYSV
7371 do {
7372 execve(cmd, argv, envp);
7373 } while (errno == EINTR);
7374#else
7375 execve(cmd, argv, envp);
7376#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007377 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007378 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007379 return;
7380 }
7381 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007382 char **ap;
7383 char **new;
7384
7385 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007386 continue;
7387 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007388 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007389 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390 ap += 2;
7391 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007392 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007393 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007394 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007395 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007396 goto repeat;
7397 }
7398}
7399
7400/*
7401 * Exec a program. Never returns. If you change this routine, you may
7402 * have to change the find_command routine as well.
7403 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007404static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007405static void
7406shellexec(char **argv, const char *path, int idx)
7407{
7408 char *cmdname;
7409 int e;
7410 char **envp;
7411 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007412#if ENABLE_FEATURE_SH_STANDALONE
7413 int applet_no = -1;
7414#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007415
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007416 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007417 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007418 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007419#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007420 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007421#endif
7422 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007423 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007424 e = errno;
7425 } else {
7426 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007427 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007428 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007429 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007430 if (errno != ENOENT && errno != ENOTDIR)
7431 e = errno;
7432 }
7433 stunalloc(cmdname);
7434 }
7435 }
7436
7437 /* Map to POSIX errors */
7438 switch (e) {
7439 case EACCES:
7440 exerrno = 126;
7441 break;
7442 case ENOENT:
7443 exerrno = 127;
7444 break;
7445 default:
7446 exerrno = 2;
7447 break;
7448 }
7449 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007450 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7451 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007452 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7453 /* NOTREACHED */
7454}
7455
7456static void
7457printentry(struct tblentry *cmdp)
7458{
7459 int idx;
7460 const char *path;
7461 char *name;
7462
7463 idx = cmdp->param.index;
7464 path = pathval();
7465 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007466 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007467 stunalloc(name);
7468 } while (--idx >= 0);
7469 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7470}
7471
7472/*
7473 * Clear out command entries. The argument specifies the first entry in
7474 * PATH which has changed.
7475 */
7476static void
7477clearcmdentry(int firstchange)
7478{
7479 struct tblentry **tblp;
7480 struct tblentry **pp;
7481 struct tblentry *cmdp;
7482
7483 INT_OFF;
7484 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7485 pp = tblp;
7486 while ((cmdp = *pp) != NULL) {
7487 if ((cmdp->cmdtype == CMDNORMAL &&
7488 cmdp->param.index >= firstchange)
7489 || (cmdp->cmdtype == CMDBUILTIN &&
7490 builtinloc >= firstchange)
7491 ) {
7492 *pp = cmdp->next;
7493 free(cmdp);
7494 } else {
7495 pp = &cmdp->next;
7496 }
7497 }
7498 }
7499 INT_ON;
7500}
7501
7502/*
7503 * Locate a command in the command hash table. If "add" is nonzero,
7504 * add the command to the table if it is not already present. The
7505 * variable "lastcmdentry" is set to point to the address of the link
7506 * pointing to the entry, so that delete_cmd_entry can delete the
7507 * entry.
7508 *
7509 * Interrupts must be off if called with add != 0.
7510 */
7511static struct tblentry **lastcmdentry;
7512
7513static struct tblentry *
7514cmdlookup(const char *name, int add)
7515{
7516 unsigned int hashval;
7517 const char *p;
7518 struct tblentry *cmdp;
7519 struct tblentry **pp;
7520
7521 p = name;
7522 hashval = (unsigned char)*p << 4;
7523 while (*p)
7524 hashval += (unsigned char)*p++;
7525 hashval &= 0x7FFF;
7526 pp = &cmdtable[hashval % CMDTABLESIZE];
7527 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7528 if (strcmp(cmdp->cmdname, name) == 0)
7529 break;
7530 pp = &cmdp->next;
7531 }
7532 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007533 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7534 + strlen(name)
7535 /* + 1 - already done because
7536 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007537 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007538 cmdp->cmdtype = CMDUNKNOWN;
7539 strcpy(cmdp->cmdname, name);
7540 }
7541 lastcmdentry = pp;
7542 return cmdp;
7543}
7544
7545/*
7546 * Delete the command entry returned on the last lookup.
7547 */
7548static void
7549delete_cmd_entry(void)
7550{
7551 struct tblentry *cmdp;
7552
7553 INT_OFF;
7554 cmdp = *lastcmdentry;
7555 *lastcmdentry = cmdp->next;
7556 if (cmdp->cmdtype == CMDFUNCTION)
7557 freefunc(cmdp->param.func);
7558 free(cmdp);
7559 INT_ON;
7560}
7561
7562/*
7563 * Add a new command entry, replacing any existing command entry for
7564 * the same name - except special builtins.
7565 */
7566static void
7567addcmdentry(char *name, struct cmdentry *entry)
7568{
7569 struct tblentry *cmdp;
7570
7571 cmdp = cmdlookup(name, 1);
7572 if (cmdp->cmdtype == CMDFUNCTION) {
7573 freefunc(cmdp->param.func);
7574 }
7575 cmdp->cmdtype = entry->cmdtype;
7576 cmdp->param = entry->u;
7577 cmdp->rehash = 0;
7578}
7579
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007580static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007581hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007582{
7583 struct tblentry **pp;
7584 struct tblentry *cmdp;
7585 int c;
7586 struct cmdentry entry;
7587 char *name;
7588
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007589 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007590 clearcmdentry(0);
7591 return 0;
7592 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007593
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007594 if (*argptr == NULL) {
7595 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7596 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7597 if (cmdp->cmdtype == CMDNORMAL)
7598 printentry(cmdp);
7599 }
7600 }
7601 return 0;
7602 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007603
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007604 c = 0;
7605 while ((name = *argptr) != NULL) {
7606 cmdp = cmdlookup(name, 0);
7607 if (cmdp != NULL
7608 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007609 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7610 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007611 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007612 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007613 find_command(name, &entry, DO_ERR, pathval());
7614 if (entry.cmdtype == CMDUNKNOWN)
7615 c = 1;
7616 argptr++;
7617 }
7618 return c;
7619}
7620
7621/*
7622 * Called when a cd is done. Marks all commands so the next time they
7623 * are executed they will be rehashed.
7624 */
7625static void
7626hashcd(void)
7627{
7628 struct tblentry **pp;
7629 struct tblentry *cmdp;
7630
7631 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7632 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007633 if (cmdp->cmdtype == CMDNORMAL
7634 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007635 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007636 && builtinloc > 0)
7637 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007638 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007639 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007640 }
7641 }
7642}
7643
7644/*
7645 * Fix command hash table when PATH changed.
7646 * Called before PATH is changed. The argument is the new value of PATH;
7647 * pathval() still returns the old value at this point.
7648 * Called with interrupts off.
7649 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007650static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007651changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007652{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007653 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007654 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007655 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007656 int idx_bltin;
7657
7658 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007659 firstchange = 9999; /* assume no change */
7660 idx = 0;
7661 idx_bltin = -1;
7662 for (;;) {
7663 if (*old != *new) {
7664 firstchange = idx;
7665 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007666 || (*old == ':' && *new == '\0')
7667 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007668 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007669 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007670 old = new; /* ignore subsequent differences */
7671 }
7672 if (*new == '\0')
7673 break;
7674 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7675 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007676 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007677 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007678 new++;
7679 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007680 }
7681 if (builtinloc < 0 && idx_bltin >= 0)
7682 builtinloc = idx_bltin; /* zap builtins */
7683 if (builtinloc >= 0 && idx_bltin < 0)
7684 firstchange = 0;
7685 clearcmdentry(firstchange);
7686 builtinloc = idx_bltin;
7687}
7688
7689#define TEOF 0
7690#define TNL 1
7691#define TREDIR 2
7692#define TWORD 3
7693#define TSEMI 4
7694#define TBACKGND 5
7695#define TAND 6
7696#define TOR 7
7697#define TPIPE 8
7698#define TLP 9
7699#define TRP 10
7700#define TENDCASE 11
7701#define TENDBQUOTE 12
7702#define TNOT 13
7703#define TCASE 14
7704#define TDO 15
7705#define TDONE 16
7706#define TELIF 17
7707#define TELSE 18
7708#define TESAC 19
7709#define TFI 20
7710#define TFOR 21
7711#define TIF 22
7712#define TIN 23
7713#define TTHEN 24
7714#define TUNTIL 25
7715#define TWHILE 26
7716#define TBEGIN 27
7717#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007718typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007719
7720/* first char is indicating which tokens mark the end of a list */
7721static const char *const tokname_array[] = {
7722 "\1end of file",
7723 "\0newline",
7724 "\0redirection",
7725 "\0word",
7726 "\0;",
7727 "\0&",
7728 "\0&&",
7729 "\0||",
7730 "\0|",
7731 "\0(",
7732 "\1)",
7733 "\1;;",
7734 "\1`",
7735#define KWDOFFSET 13
7736 /* the following are keywords */
7737 "\0!",
7738 "\0case",
7739 "\1do",
7740 "\1done",
7741 "\1elif",
7742 "\1else",
7743 "\1esac",
7744 "\1fi",
7745 "\0for",
7746 "\0if",
7747 "\0in",
7748 "\1then",
7749 "\0until",
7750 "\0while",
7751 "\0{",
7752 "\1}",
7753};
7754
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007755/* Wrapper around strcmp for qsort/bsearch/... */
7756static int
7757pstrcmp(const void *a, const void *b)
7758{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007759 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007760}
7761
7762static const char *const *
7763findkwd(const char *s)
7764{
7765 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007766 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7767 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007768}
7769
7770/*
7771 * Locate and print what a word is...
7772 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007773static int
7774describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007775{
7776 struct cmdentry entry;
7777 struct tblentry *cmdp;
7778#if ENABLE_ASH_ALIAS
7779 const struct alias *ap;
7780#endif
7781 const char *path = pathval();
7782
7783 if (describe_command_verbose) {
7784 out1str(command);
7785 }
7786
7787 /* First look at the keywords */
7788 if (findkwd(command)) {
7789 out1str(describe_command_verbose ? " is a shell keyword" : command);
7790 goto out;
7791 }
7792
7793#if ENABLE_ASH_ALIAS
7794 /* Then look at the aliases */
7795 ap = lookupalias(command, 0);
7796 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007797 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007798 out1str("alias ");
7799 printalias(ap);
7800 return 0;
7801 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007802 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007803 goto out;
7804 }
7805#endif
7806 /* Then check if it is a tracked alias */
7807 cmdp = cmdlookup(command, 0);
7808 if (cmdp != NULL) {
7809 entry.cmdtype = cmdp->cmdtype;
7810 entry.u = cmdp->param;
7811 } else {
7812 /* Finally use brute force */
7813 find_command(command, &entry, DO_ABS, path);
7814 }
7815
7816 switch (entry.cmdtype) {
7817 case CMDNORMAL: {
7818 int j = entry.u.index;
7819 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007820 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007821 p = command;
7822 } else {
7823 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007824 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007825 stunalloc(p);
7826 } while (--j >= 0);
7827 }
7828 if (describe_command_verbose) {
7829 out1fmt(" is%s %s",
7830 (cmdp ? " a tracked alias for" : nullstr), p
7831 );
7832 } else {
7833 out1str(p);
7834 }
7835 break;
7836 }
7837
7838 case CMDFUNCTION:
7839 if (describe_command_verbose) {
7840 out1str(" is a shell function");
7841 } else {
7842 out1str(command);
7843 }
7844 break;
7845
7846 case CMDBUILTIN:
7847 if (describe_command_verbose) {
7848 out1fmt(" is a %sshell builtin",
7849 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7850 "special " : nullstr
7851 );
7852 } else {
7853 out1str(command);
7854 }
7855 break;
7856
7857 default:
7858 if (describe_command_verbose) {
7859 out1str(": not found\n");
7860 }
7861 return 127;
7862 }
7863 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007864 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007865 return 0;
7866}
7867
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007868static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007869typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007870{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007871 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007872 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007873 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007874
Denis Vlasenko46846e22007-05-20 13:08:31 +00007875 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007876 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007877 i++;
7878 verbose = 0;
7879 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007880 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007881 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007882 }
7883 return err;
7884}
7885
7886#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007887static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007888commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007889{
7890 int c;
7891 enum {
7892 VERIFY_BRIEF = 1,
7893 VERIFY_VERBOSE = 2,
7894 } verify = 0;
7895
7896 while ((c = nextopt("pvV")) != '\0')
7897 if (c == 'V')
7898 verify |= VERIFY_VERBOSE;
7899 else if (c == 'v')
7900 verify |= VERIFY_BRIEF;
7901#if DEBUG
7902 else if (c != 'p')
7903 abort();
7904#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007905 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7906 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007907 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007908 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007909
7910 return 0;
7911}
7912#endif
7913
7914
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007915/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007916
Denis Vlasenko340299a2008-11-21 10:36:36 +00007917static int funcblocksize; /* size of structures in function */
7918static int funcstringsize; /* size of strings in node */
7919static void *funcblock; /* block to allocate function from */
7920static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007921
Eric Andersencb57d552001-06-28 07:25:16 +00007922/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007923#define EV_EXIT 01 /* exit after evaluating tree */
7924#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007925#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007926
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007927static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007928 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7929 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7930 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7931 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7932 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7933 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7934 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7935 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7936 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7937 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7938 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7939 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7940 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7941 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7942 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7943 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7944 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007945#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007946 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007947#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007948 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7949 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7950 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7951 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7952 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7953 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7954 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7955 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7956 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007957};
7958
7959static void calcsize(union node *n);
7960
7961static void
7962sizenodelist(struct nodelist *lp)
7963{
7964 while (lp) {
7965 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7966 calcsize(lp->n);
7967 lp = lp->next;
7968 }
7969}
7970
7971static void
7972calcsize(union node *n)
7973{
7974 if (n == NULL)
7975 return;
7976 funcblocksize += nodesize[n->type];
7977 switch (n->type) {
7978 case NCMD:
7979 calcsize(n->ncmd.redirect);
7980 calcsize(n->ncmd.args);
7981 calcsize(n->ncmd.assign);
7982 break;
7983 case NPIPE:
7984 sizenodelist(n->npipe.cmdlist);
7985 break;
7986 case NREDIR:
7987 case NBACKGND:
7988 case NSUBSHELL:
7989 calcsize(n->nredir.redirect);
7990 calcsize(n->nredir.n);
7991 break;
7992 case NAND:
7993 case NOR:
7994 case NSEMI:
7995 case NWHILE:
7996 case NUNTIL:
7997 calcsize(n->nbinary.ch2);
7998 calcsize(n->nbinary.ch1);
7999 break;
8000 case NIF:
8001 calcsize(n->nif.elsepart);
8002 calcsize(n->nif.ifpart);
8003 calcsize(n->nif.test);
8004 break;
8005 case NFOR:
8006 funcstringsize += strlen(n->nfor.var) + 1;
8007 calcsize(n->nfor.body);
8008 calcsize(n->nfor.args);
8009 break;
8010 case NCASE:
8011 calcsize(n->ncase.cases);
8012 calcsize(n->ncase.expr);
8013 break;
8014 case NCLIST:
8015 calcsize(n->nclist.body);
8016 calcsize(n->nclist.pattern);
8017 calcsize(n->nclist.next);
8018 break;
8019 case NDEFUN:
8020 case NARG:
8021 sizenodelist(n->narg.backquote);
8022 funcstringsize += strlen(n->narg.text) + 1;
8023 calcsize(n->narg.next);
8024 break;
8025 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008026#if ENABLE_ASH_BASH_COMPAT
8027 case NTO2:
8028#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008029 case NCLOBBER:
8030 case NFROM:
8031 case NFROMTO:
8032 case NAPPEND:
8033 calcsize(n->nfile.fname);
8034 calcsize(n->nfile.next);
8035 break;
8036 case NTOFD:
8037 case NFROMFD:
8038 calcsize(n->ndup.vname);
8039 calcsize(n->ndup.next);
8040 break;
8041 case NHERE:
8042 case NXHERE:
8043 calcsize(n->nhere.doc);
8044 calcsize(n->nhere.next);
8045 break;
8046 case NNOT:
8047 calcsize(n->nnot.com);
8048 break;
8049 };
8050}
8051
8052static char *
8053nodeckstrdup(char *s)
8054{
8055 char *rtn = funcstring;
8056
8057 strcpy(funcstring, s);
8058 funcstring += strlen(s) + 1;
8059 return rtn;
8060}
8061
8062static union node *copynode(union node *);
8063
8064static struct nodelist *
8065copynodelist(struct nodelist *lp)
8066{
8067 struct nodelist *start;
8068 struct nodelist **lpp;
8069
8070 lpp = &start;
8071 while (lp) {
8072 *lpp = funcblock;
8073 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8074 (*lpp)->n = copynode(lp->n);
8075 lp = lp->next;
8076 lpp = &(*lpp)->next;
8077 }
8078 *lpp = NULL;
8079 return start;
8080}
8081
8082static union node *
8083copynode(union node *n)
8084{
8085 union node *new;
8086
8087 if (n == NULL)
8088 return NULL;
8089 new = funcblock;
8090 funcblock = (char *) funcblock + nodesize[n->type];
8091
8092 switch (n->type) {
8093 case NCMD:
8094 new->ncmd.redirect = copynode(n->ncmd.redirect);
8095 new->ncmd.args = copynode(n->ncmd.args);
8096 new->ncmd.assign = copynode(n->ncmd.assign);
8097 break;
8098 case NPIPE:
8099 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008100 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008101 break;
8102 case NREDIR:
8103 case NBACKGND:
8104 case NSUBSHELL:
8105 new->nredir.redirect = copynode(n->nredir.redirect);
8106 new->nredir.n = copynode(n->nredir.n);
8107 break;
8108 case NAND:
8109 case NOR:
8110 case NSEMI:
8111 case NWHILE:
8112 case NUNTIL:
8113 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8114 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8115 break;
8116 case NIF:
8117 new->nif.elsepart = copynode(n->nif.elsepart);
8118 new->nif.ifpart = copynode(n->nif.ifpart);
8119 new->nif.test = copynode(n->nif.test);
8120 break;
8121 case NFOR:
8122 new->nfor.var = nodeckstrdup(n->nfor.var);
8123 new->nfor.body = copynode(n->nfor.body);
8124 new->nfor.args = copynode(n->nfor.args);
8125 break;
8126 case NCASE:
8127 new->ncase.cases = copynode(n->ncase.cases);
8128 new->ncase.expr = copynode(n->ncase.expr);
8129 break;
8130 case NCLIST:
8131 new->nclist.body = copynode(n->nclist.body);
8132 new->nclist.pattern = copynode(n->nclist.pattern);
8133 new->nclist.next = copynode(n->nclist.next);
8134 break;
8135 case NDEFUN:
8136 case NARG:
8137 new->narg.backquote = copynodelist(n->narg.backquote);
8138 new->narg.text = nodeckstrdup(n->narg.text);
8139 new->narg.next = copynode(n->narg.next);
8140 break;
8141 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008142#if ENABLE_ASH_BASH_COMPAT
8143 case NTO2:
8144#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008145 case NCLOBBER:
8146 case NFROM:
8147 case NFROMTO:
8148 case NAPPEND:
8149 new->nfile.fname = copynode(n->nfile.fname);
8150 new->nfile.fd = n->nfile.fd;
8151 new->nfile.next = copynode(n->nfile.next);
8152 break;
8153 case NTOFD:
8154 case NFROMFD:
8155 new->ndup.vname = copynode(n->ndup.vname);
8156 new->ndup.dupfd = n->ndup.dupfd;
8157 new->ndup.fd = n->ndup.fd;
8158 new->ndup.next = copynode(n->ndup.next);
8159 break;
8160 case NHERE:
8161 case NXHERE:
8162 new->nhere.doc = copynode(n->nhere.doc);
8163 new->nhere.fd = n->nhere.fd;
8164 new->nhere.next = copynode(n->nhere.next);
8165 break;
8166 case NNOT:
8167 new->nnot.com = copynode(n->nnot.com);
8168 break;
8169 };
8170 new->type = n->type;
8171 return new;
8172}
8173
8174/*
8175 * Make a copy of a parse tree.
8176 */
8177static struct funcnode *
8178copyfunc(union node *n)
8179{
8180 struct funcnode *f;
8181 size_t blocksize;
8182
8183 funcblocksize = offsetof(struct funcnode, n);
8184 funcstringsize = 0;
8185 calcsize(n);
8186 blocksize = funcblocksize;
8187 f = ckmalloc(blocksize + funcstringsize);
8188 funcblock = (char *) f + offsetof(struct funcnode, n);
8189 funcstring = (char *) f + blocksize;
8190 copynode(n);
8191 f->count = 0;
8192 return f;
8193}
8194
8195/*
8196 * Define a shell function.
8197 */
8198static void
8199defun(char *name, union node *func)
8200{
8201 struct cmdentry entry;
8202
8203 INT_OFF;
8204 entry.cmdtype = CMDFUNCTION;
8205 entry.u.func = copyfunc(func);
8206 addcmdentry(name, &entry);
8207 INT_ON;
8208}
8209
Denis Vlasenko4b875702009-03-19 13:30:04 +00008210/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008211#define SKIPBREAK (1 << 0)
8212#define SKIPCONT (1 << 1)
8213#define SKIPFUNC (1 << 2)
8214#define SKIPFILE (1 << 3)
8215#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008216static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008217static int skipcount; /* number of levels to skip */
8218static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008219static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008220
Denis Vlasenko4b875702009-03-19 13:30:04 +00008221/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008222static int evalstring(char *s, int mask);
8223
Denis Vlasenko4b875702009-03-19 13:30:04 +00008224/* Called to execute a trap.
8225 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008226 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008227 *
8228 * Perhaps we should avoid entering new trap handlers
8229 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008230 */
8231static int
8232dotrap(void)
8233{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008234 uint8_t *g;
8235 int sig;
8236 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008237
8238 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008239 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008240 xbarrier();
8241
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008242 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008243 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8244 int want_exexit;
8245 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008246
Denis Vlasenko4b875702009-03-19 13:30:04 +00008247 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008248 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008249 t = trap[sig];
8250 /* non-trapped SIGINT is handled separately by raise_interrupt,
8251 * don't upset it by resetting gotsig[SIGINT-1] */
8252 if (sig == SIGINT && !t)
8253 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008254
8255 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008256 *g = 0;
8257 if (!t)
8258 continue;
8259 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008260 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008261 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008262 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008263 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008264 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008265 }
8266
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008267 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008268 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008269}
8270
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008271/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008272static void evalloop(union node *, int);
8273static void evalfor(union node *, int);
8274static void evalcase(union node *, int);
8275static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008276static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008277static void evalpipe(union node *, int);
8278static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008279static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008280static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008281
Eric Andersen62483552001-07-10 06:09:16 +00008282/*
Eric Andersenc470f442003-07-28 09:56:35 +00008283 * Evaluate a parse tree. The value is left in the global variable
8284 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008285 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008286static void
Eric Andersenc470f442003-07-28 09:56:35 +00008287evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008288{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008289 struct jmploc *volatile savehandler = exception_handler;
8290 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008291 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008292 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008293 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008294 int int_level;
8295
8296 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008297
Eric Andersenc470f442003-07-28 09:56:35 +00008298 if (n == NULL) {
8299 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008300 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008301 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008302 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008303
8304 exception_handler = &jmploc;
8305 {
8306 int err = setjmp(jmploc.loc);
8307 if (err) {
8308 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008309 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008310 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8311 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008312 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008313 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008314 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008315 TRACE(("exception %d in evaltree, propagating err=%d\n",
8316 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008317 exception_handler = savehandler;
8318 longjmp(exception_handler->loc, err);
8319 }
8320 }
8321
Eric Andersenc470f442003-07-28 09:56:35 +00008322 switch (n->type) {
8323 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008324#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008325 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008326 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008327 break;
8328#endif
8329 case NNOT:
8330 evaltree(n->nnot.com, EV_TESTED);
8331 status = !exitstatus;
8332 goto setstatus;
8333 case NREDIR:
8334 expredir(n->nredir.redirect);
8335 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8336 if (!status) {
8337 evaltree(n->nredir.n, flags & EV_TESTED);
8338 status = exitstatus;
8339 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008340 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008341 goto setstatus;
8342 case NCMD:
8343 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008344 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008345 if (eflag && !(flags & EV_TESTED))
8346 checkexit = ~0;
8347 goto calleval;
8348 case NFOR:
8349 evalfn = evalfor;
8350 goto calleval;
8351 case NWHILE:
8352 case NUNTIL:
8353 evalfn = evalloop;
8354 goto calleval;
8355 case NSUBSHELL:
8356 case NBACKGND:
8357 evalfn = evalsubshell;
8358 goto calleval;
8359 case NPIPE:
8360 evalfn = evalpipe;
8361 goto checkexit;
8362 case NCASE:
8363 evalfn = evalcase;
8364 goto calleval;
8365 case NAND:
8366 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008367 case NSEMI: {
8368
Eric Andersenc470f442003-07-28 09:56:35 +00008369#if NAND + 1 != NOR
8370#error NAND + 1 != NOR
8371#endif
8372#if NOR + 1 != NSEMI
8373#error NOR + 1 != NSEMI
8374#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008375 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008376 evaltree(
8377 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008378 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008379 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008380 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008381 break;
8382 if (!evalskip) {
8383 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008384 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008385 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008386 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008387 evalfn(n, flags);
8388 break;
8389 }
8390 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008391 }
Eric Andersenc470f442003-07-28 09:56:35 +00008392 case NIF:
8393 evaltree(n->nif.test, EV_TESTED);
8394 if (evalskip)
8395 break;
8396 if (exitstatus == 0) {
8397 n = n->nif.ifpart;
8398 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008399 }
8400 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008401 n = n->nif.elsepart;
8402 goto evaln;
8403 }
8404 goto success;
8405 case NDEFUN:
8406 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008407 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008408 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008409 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008410 exitstatus = status;
8411 break;
8412 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008413
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008414 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008415 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008416
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008417 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008418 /* Order of checks below is important:
8419 * signal handlers trigger before exit caused by "set -e".
8420 */
8421 if (pending_sig && dotrap())
8422 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008423 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008424 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008425
8426 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008427 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008428 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008429 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008430
8431 RESTORE_INT(int_level);
8432 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008433}
8434
Eric Andersenc470f442003-07-28 09:56:35 +00008435#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8436static
8437#endif
8438void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8439
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008440static void
Eric Andersenc470f442003-07-28 09:56:35 +00008441evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008442{
8443 int status;
8444
8445 loopnest++;
8446 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008447 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008448 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008449 int i;
8450
Eric Andersencb57d552001-06-28 07:25:16 +00008451 evaltree(n->nbinary.ch1, EV_TESTED);
8452 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008453 skipping:
8454 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008455 evalskip = 0;
8456 continue;
8457 }
8458 if (evalskip == SKIPBREAK && --skipcount <= 0)
8459 evalskip = 0;
8460 break;
8461 }
Eric Andersenc470f442003-07-28 09:56:35 +00008462 i = exitstatus;
8463 if (n->type != NWHILE)
8464 i = !i;
8465 if (i != 0)
8466 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008467 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008468 status = exitstatus;
8469 if (evalskip)
8470 goto skipping;
8471 }
8472 loopnest--;
8473 exitstatus = status;
8474}
8475
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008476static void
Eric Andersenc470f442003-07-28 09:56:35 +00008477evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008478{
8479 struct arglist arglist;
8480 union node *argp;
8481 struct strlist *sp;
8482 struct stackmark smark;
8483
8484 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008485 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008486 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008487 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008488 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008489 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008490 if (evalskip)
8491 goto out;
8492 }
8493 *arglist.lastp = NULL;
8494
8495 exitstatus = 0;
8496 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008497 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008498 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008499 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008500 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008501 if (evalskip) {
8502 if (evalskip == SKIPCONT && --skipcount <= 0) {
8503 evalskip = 0;
8504 continue;
8505 }
8506 if (evalskip == SKIPBREAK && --skipcount <= 0)
8507 evalskip = 0;
8508 break;
8509 }
8510 }
8511 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008512 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008513 popstackmark(&smark);
8514}
8515
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008516static void
Eric Andersenc470f442003-07-28 09:56:35 +00008517evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008518{
8519 union node *cp;
8520 union node *patp;
8521 struct arglist arglist;
8522 struct stackmark smark;
8523
8524 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008525 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008526 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008527 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008528 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008529 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8530 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008531 if (casematch(patp, arglist.list->text)) {
8532 if (evalskip == 0) {
8533 evaltree(cp->nclist.body, flags);
8534 }
8535 goto out;
8536 }
8537 }
8538 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008539 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008540 popstackmark(&smark);
8541}
8542
Eric Andersenc470f442003-07-28 09:56:35 +00008543/*
8544 * Kick off a subshell to evaluate a tree.
8545 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008546static void
Eric Andersenc470f442003-07-28 09:56:35 +00008547evalsubshell(union node *n, int flags)
8548{
8549 struct job *jp;
8550 int backgnd = (n->type == NBACKGND);
8551 int status;
8552
8553 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008554 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008555 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008556 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008557 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008558 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008559 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008560 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008561 flags |= EV_EXIT;
8562 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008563 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008564 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008565 redirect(n->nredir.redirect, 0);
8566 evaltreenr(n->nredir.n, flags);
8567 /* never returns */
8568 }
8569 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008570 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008571 status = waitforjob(jp);
8572 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008573 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008574}
8575
Eric Andersenc470f442003-07-28 09:56:35 +00008576/*
8577 * Compute the names of the files in a redirection list.
8578 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008579static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008580static void
8581expredir(union node *n)
8582{
8583 union node *redir;
8584
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008585 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008586 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008587
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008588 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008589 fn.lastp = &fn.list;
8590 switch (redir->type) {
8591 case NFROMTO:
8592 case NFROM:
8593 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008594#if ENABLE_ASH_BASH_COMPAT
8595 case NTO2:
8596#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008597 case NCLOBBER:
8598 case NAPPEND:
8599 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008600#if ENABLE_ASH_BASH_COMPAT
8601 store_expfname:
8602#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008603 redir->nfile.expfname = fn.list->text;
8604 break;
8605 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008606 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008607 if (redir->ndup.vname) {
8608 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008609 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008610 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008611#if ENABLE_ASH_BASH_COMPAT
8612//FIXME: we used expandarg with different args!
8613 if (!isdigit_str9(fn.list->text)) {
8614 /* >&file, not >&fd */
8615 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8616 ash_msg_and_raise_error("redir error");
8617 redir->type = NTO2;
8618 goto store_expfname;
8619 }
8620#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008621 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008622 }
8623 break;
8624 }
8625 }
8626}
8627
Eric Andersencb57d552001-06-28 07:25:16 +00008628/*
Eric Andersencb57d552001-06-28 07:25:16 +00008629 * Evaluate a pipeline. All the processes in the pipeline are children
8630 * of the process creating the pipeline. (This differs from some versions
8631 * of the shell, which make the last process in a pipeline the parent
8632 * of all the rest.)
8633 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008634static void
Eric Andersenc470f442003-07-28 09:56:35 +00008635evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008636{
8637 struct job *jp;
8638 struct nodelist *lp;
8639 int pipelen;
8640 int prevfd;
8641 int pip[2];
8642
Eric Andersenc470f442003-07-28 09:56:35 +00008643 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008644 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008645 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008646 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008647 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008648 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008649 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008650 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008651 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008652 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008653 pip[1] = -1;
8654 if (lp->next) {
8655 if (pipe(pip) < 0) {
8656 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008657 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008658 }
8659 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008660 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008661 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008662 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008663 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008664 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008665 if (prevfd > 0) {
8666 dup2(prevfd, 0);
8667 close(prevfd);
8668 }
8669 if (pip[1] > 1) {
8670 dup2(pip[1], 1);
8671 close(pip[1]);
8672 }
Eric Andersenc470f442003-07-28 09:56:35 +00008673 evaltreenr(lp->n, flags);
8674 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008675 }
8676 if (prevfd >= 0)
8677 close(prevfd);
8678 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008679 /* Don't want to trigger debugging */
8680 if (pip[1] != -1)
8681 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008682 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008683 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008684 exitstatus = waitforjob(jp);
8685 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008686 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008687 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008688}
8689
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008690/*
8691 * Controls whether the shell is interactive or not.
8692 */
8693static void
8694setinteractive(int on)
8695{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008696 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008697
8698 if (++on == is_interactive)
8699 return;
8700 is_interactive = on;
8701 setsignal(SIGINT);
8702 setsignal(SIGQUIT);
8703 setsignal(SIGTERM);
8704#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8705 if (is_interactive > 1) {
8706 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008707 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008708
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008709 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008710 /* note: ash and hush share this string */
8711 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008712 "Enter 'help' for a list of built-in commands."
8713 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008714 bb_banner,
8715 "built-in shell (ash)"
8716 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008717 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008718 }
8719 }
8720#endif
8721}
8722
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008723static void
8724optschanged(void)
8725{
8726#if DEBUG
8727 opentrace();
8728#endif
8729 setinteractive(iflag);
8730 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008731#if ENABLE_FEATURE_EDITING_VI
8732 if (viflag)
8733 line_input_state->flags |= VI_MODE;
8734 else
8735 line_input_state->flags &= ~VI_MODE;
8736#else
8737 viflag = 0; /* forcibly keep the option off */
8738#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008739}
8740
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008741static struct localvar *localvars;
8742
8743/*
8744 * Called after a function returns.
8745 * Interrupts must be off.
8746 */
8747static void
8748poplocalvars(void)
8749{
8750 struct localvar *lvp;
8751 struct var *vp;
8752
8753 while ((lvp = localvars) != NULL) {
8754 localvars = lvp->next;
8755 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008756 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008757 if (vp == NULL) { /* $- saved */
8758 memcpy(optlist, lvp->text, sizeof(optlist));
8759 free((char*)lvp->text);
8760 optschanged();
8761 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008762 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008763 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008764 if (vp->var_func)
8765 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008766 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008767 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008768 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008769 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008770 }
8771 free(lvp);
8772 }
8773}
8774
8775static int
8776evalfun(struct funcnode *func, int argc, char **argv, int flags)
8777{
8778 volatile struct shparam saveparam;
8779 struct localvar *volatile savelocalvars;
8780 struct jmploc *volatile savehandler;
8781 struct jmploc jmploc;
8782 int e;
8783
8784 saveparam = shellparam;
8785 savelocalvars = localvars;
8786 e = setjmp(jmploc.loc);
8787 if (e) {
8788 goto funcdone;
8789 }
8790 INT_OFF;
8791 savehandler = exception_handler;
8792 exception_handler = &jmploc;
8793 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008794 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008795 func->count++;
8796 funcnest++;
8797 INT_ON;
8798 shellparam.nparam = argc - 1;
8799 shellparam.p = argv + 1;
8800#if ENABLE_ASH_GETOPTS
8801 shellparam.optind = 1;
8802 shellparam.optoff = -1;
8803#endif
8804 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008805 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008806 INT_OFF;
8807 funcnest--;
8808 freefunc(func);
8809 poplocalvars();
8810 localvars = savelocalvars;
8811 freeparam(&shellparam);
8812 shellparam = saveparam;
8813 exception_handler = savehandler;
8814 INT_ON;
8815 evalskip &= ~SKIPFUNC;
8816 return e;
8817}
8818
Denis Vlasenko131ae172007-02-18 13:00:19 +00008819#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008820static char **
8821parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008822{
8823 char *cp, c;
8824
8825 for (;;) {
8826 cp = *++argv;
8827 if (!cp)
8828 return 0;
8829 if (*cp++ != '-')
8830 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008831 c = *cp++;
8832 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008833 break;
8834 if (c == '-' && !*cp) {
8835 argv++;
8836 break;
8837 }
8838 do {
8839 switch (c) {
8840 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008841 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008842 break;
8843 default:
8844 /* run 'typecmd' for other options */
8845 return 0;
8846 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008847 c = *cp++;
8848 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008849 }
8850 return argv;
8851}
8852#endif
8853
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008854/*
8855 * Make a variable a local variable. When a variable is made local, it's
8856 * value and flags are saved in a localvar structure. The saved values
8857 * will be restored when the shell function returns. We handle the name
8858 * "-" as a special case.
8859 */
8860static void
8861mklocal(char *name)
8862{
8863 struct localvar *lvp;
8864 struct var **vpp;
8865 struct var *vp;
8866
8867 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008868 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008869 if (LONE_DASH(name)) {
8870 char *p;
8871 p = ckmalloc(sizeof(optlist));
8872 lvp->text = memcpy(p, optlist, sizeof(optlist));
8873 vp = NULL;
8874 } else {
8875 char *eq;
8876
8877 vpp = hashvar(name);
8878 vp = *findvar(vpp, name);
8879 eq = strchr(name, '=');
8880 if (vp == NULL) {
8881 if (eq)
8882 setvareq(name, VSTRFIXED);
8883 else
8884 setvar(name, NULL, VSTRFIXED);
8885 vp = *vpp; /* the new variable */
8886 lvp->flags = VUNSET;
8887 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008888 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008889 lvp->flags = vp->flags;
8890 vp->flags |= VSTRFIXED|VTEXTFIXED;
8891 if (eq)
8892 setvareq(name, 0);
8893 }
8894 }
8895 lvp->vp = vp;
8896 lvp->next = localvars;
8897 localvars = lvp;
8898 INT_ON;
8899}
8900
8901/*
8902 * The "local" command.
8903 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008904static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008905localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008906{
8907 char *name;
8908
8909 argv = argptr;
8910 while ((name = *argv++) != NULL) {
8911 mklocal(name);
8912 }
8913 return 0;
8914}
8915
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008916static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008917falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008918{
8919 return 1;
8920}
8921
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008922static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008923truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008924{
8925 return 0;
8926}
8927
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008928static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008929execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008930{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008931 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008932 iflag = 0; /* exit on error */
8933 mflag = 0;
8934 optschanged();
8935 shellexec(argv + 1, pathval(), 0);
8936 }
8937 return 0;
8938}
8939
8940/*
8941 * The return command.
8942 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008943static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008944returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008945{
8946 /*
8947 * If called outside a function, do what ksh does;
8948 * skip the rest of the file.
8949 */
8950 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8951 return argv[1] ? number(argv[1]) : exitstatus;
8952}
8953
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008954/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008955static int breakcmd(int, char **) FAST_FUNC;
8956static int dotcmd(int, char **) FAST_FUNC;
8957static int evalcmd(int, char **) FAST_FUNC;
8958static int exitcmd(int, char **) FAST_FUNC;
8959static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008960#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008961static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008962#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008963#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008964static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00008965#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00008966#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008967static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008968#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008969static int readcmd(int, char **) FAST_FUNC;
8970static int setcmd(int, char **) FAST_FUNC;
8971static int shiftcmd(int, char **) FAST_FUNC;
8972static int timescmd(int, char **) FAST_FUNC;
8973static int trapcmd(int, char **) FAST_FUNC;
8974static int umaskcmd(int, char **) FAST_FUNC;
8975static int unsetcmd(int, char **) FAST_FUNC;
8976static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008977
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008978#define BUILTIN_NOSPEC "0"
8979#define BUILTIN_SPECIAL "1"
8980#define BUILTIN_REGULAR "2"
8981#define BUILTIN_SPEC_REG "3"
8982#define BUILTIN_ASSIGN "4"
8983#define BUILTIN_SPEC_ASSG "5"
8984#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008985#define BUILTIN_SPEC_REG_ASSG "7"
8986
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008987/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008988#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008989static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008990#endif
8991#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008992static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008993#endif
8994#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008995static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008996#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00008997
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008998/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008999static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009000 { BUILTIN_SPEC_REG "." , dotcmd },
9001 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009002#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009003 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009004#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009005 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009006#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009007#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009008#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009009 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009010#endif
9011#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009012 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009013#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009014 { BUILTIN_SPEC_REG "break" , breakcmd },
9015 { BUILTIN_REGULAR "cd" , cdcmd },
9016 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009017#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009018 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009019#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009020 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009021#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009022 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009023#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009024 { BUILTIN_SPEC_REG "eval" , evalcmd },
9025 { BUILTIN_SPEC_REG "exec" , execcmd },
9026 { BUILTIN_SPEC_REG "exit" , exitcmd },
9027 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9028 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009029#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009030 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009031#endif
9032#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009033 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009034#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009035 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009036#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009037 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009038#endif
9039#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009040 { BUILTIN_REGULAR "jobs" , jobscmd },
9041 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009042#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009043#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009044 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009045#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009046 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009047#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009048 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009049#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009050 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9051 { BUILTIN_REGULAR "read" , readcmd },
9052 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9053 { BUILTIN_SPEC_REG "return" , returncmd },
9054 { BUILTIN_SPEC_REG "set" , setcmd },
9055 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009056#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009057 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009058#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009059#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009060 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009061#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009062 { BUILTIN_SPEC_REG "times" , timescmd },
9063 { BUILTIN_SPEC_REG "trap" , trapcmd },
9064 { BUILTIN_REGULAR "true" , truecmd },
9065 { BUILTIN_NOSPEC "type" , typecmd },
9066 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9067 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009068#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009069 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9072 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009073};
9074
Denis Vlasenko80591b02008-03-25 07:49:43 +00009075/* Should match the above table! */
9076#define COMMANDCMD (builtintab + \
9077 2 + \
9078 1 * ENABLE_ASH_BUILTIN_TEST + \
9079 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9080 1 * ENABLE_ASH_ALIAS + \
9081 1 * ENABLE_ASH_JOB_CONTROL + \
9082 3)
9083#define EXECCMD (builtintab + \
9084 2 + \
9085 1 * ENABLE_ASH_BUILTIN_TEST + \
9086 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9087 1 * ENABLE_ASH_ALIAS + \
9088 1 * ENABLE_ASH_JOB_CONTROL + \
9089 3 + \
9090 1 * ENABLE_ASH_CMDCMD + \
9091 1 + \
9092 ENABLE_ASH_BUILTIN_ECHO + \
9093 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009094
9095/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009096 * Search the table of builtin commands.
9097 */
9098static struct builtincmd *
9099find_builtin(const char *name)
9100{
9101 struct builtincmd *bp;
9102
9103 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009104 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009105 pstrcmp
9106 );
9107 return bp;
9108}
9109
9110/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009111 * Execute a simple command.
9112 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009113static int
9114isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009115{
9116 const char *q = endofname(p);
9117 if (p == q)
9118 return 0;
9119 return *q == '=';
9120}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009121static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009122bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009123{
9124 /* Preserve exitstatus of a previous possible redirection
9125 * as POSIX mandates */
9126 return back_exitstatus;
9127}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009128static void
Eric Andersenc470f442003-07-28 09:56:35 +00009129evalcommand(union node *cmd, int flags)
9130{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009131 static const struct builtincmd null_bltin = {
9132 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009133 };
Eric Andersenc470f442003-07-28 09:56:35 +00009134 struct stackmark smark;
9135 union node *argp;
9136 struct arglist arglist;
9137 struct arglist varlist;
9138 char **argv;
9139 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009140 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009141 struct cmdentry cmdentry;
9142 struct job *jp;
9143 char *lastarg;
9144 const char *path;
9145 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009146 int status;
9147 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009148 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009149 smallint cmd_is_exec;
9150 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009151
9152 /* First expand the arguments. */
9153 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9154 setstackmark(&smark);
9155 back_exitstatus = 0;
9156
9157 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009158 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009159 varlist.lastp = &varlist.list;
9160 *varlist.lastp = NULL;
9161 arglist.lastp = &arglist.list;
9162 *arglist.lastp = NULL;
9163
9164 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009165 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009166 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9167 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9168 }
9169
Eric Andersenc470f442003-07-28 09:56:35 +00009170 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9171 struct strlist **spp;
9172
9173 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009174 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009175 expandarg(argp, &arglist, EXP_VARTILDE);
9176 else
9177 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9178
Eric Andersenc470f442003-07-28 09:56:35 +00009179 for (sp = *spp; sp; sp = sp->next)
9180 argc++;
9181 }
9182
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009183 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009184 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009185 TRACE(("evalcommand arg: %s\n", sp->text));
9186 *nargv++ = sp->text;
9187 }
9188 *nargv = NULL;
9189
9190 lastarg = NULL;
9191 if (iflag && funcnest == 0 && argc > 0)
9192 lastarg = nargv[-1];
9193
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009194 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009195 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009196 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009197
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009198 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009199 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9200 struct strlist **spp;
9201 char *p;
9202
9203 spp = varlist.lastp;
9204 expandarg(argp, &varlist, EXP_VARTILDE);
9205
9206 /*
9207 * Modify the command lookup path, if a PATH= assignment
9208 * is present
9209 */
9210 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009211 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009212 path = p;
9213 }
9214
9215 /* Print the command if xflag is set. */
9216 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009217 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009218 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009219
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009220 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009221 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009222 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009223 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009224 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009225 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009226 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009227 }
9228 sp = arglist.list;
9229 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009230 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009231 }
9232
9233 cmd_is_exec = 0;
9234 spclbltin = -1;
9235
9236 /* Now locate the command. */
9237 if (argc) {
9238 const char *oldpath;
9239 int cmd_flag = DO_ERR;
9240
9241 path += 5;
9242 oldpath = path;
9243 for (;;) {
9244 find_command(argv[0], &cmdentry, cmd_flag, path);
9245 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009246 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009247 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009248 goto bail;
9249 }
9250
9251 /* implement bltin and command here */
9252 if (cmdentry.cmdtype != CMDBUILTIN)
9253 break;
9254 if (spclbltin < 0)
9255 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9256 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009257 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009258#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009259 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009260 path = oldpath;
9261 nargv = parse_command_args(argv, &path);
9262 if (!nargv)
9263 break;
9264 argc -= nargv - argv;
9265 argv = nargv;
9266 cmd_flag |= DO_NOFUNC;
9267 } else
9268#endif
9269 break;
9270 }
9271 }
9272
9273 if (status) {
9274 /* We have a redirection error. */
9275 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009276 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009277 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009278 exitstatus = status;
9279 goto out;
9280 }
9281
9282 /* Execute the command. */
9283 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009284 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009285
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009286#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009287/* (1) BUG: if variables are set, we need to fork, or save/restore them
9288 * around run_nofork_applet() call.
9289 * (2) Should this check also be done in forkshell()?
9290 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9291 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009292 /* find_command() encodes applet_no as (-2 - applet_no) */
9293 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009294 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009295 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009296 /* run <applet>_main() */
9297 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009298 break;
9299 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009300#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009301 /* Can we avoid forking off? For example, very last command
9302 * in a script or a subshell does not need forking,
9303 * we can just exec it.
9304 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009305 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009306 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009307 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009308 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009309 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009310 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009311 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009312 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009313 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009314 break;
9315 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009316 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009317 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009318 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009319 }
9320 listsetvar(varlist.list, VEXPORT|VSTACK);
9321 shellexec(argv, path, cmdentry.u.index);
9322 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009323 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009324 case CMDBUILTIN:
9325 cmdenviron = varlist.list;
9326 if (cmdenviron) {
9327 struct strlist *list = cmdenviron;
9328 int i = VNOSET;
9329 if (spclbltin > 0 || argc == 0) {
9330 i = 0;
9331 if (cmd_is_exec && argc > 1)
9332 i = VEXPORT;
9333 }
9334 listsetvar(list, i);
9335 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009336 /* Tight loop with builtins only:
9337 * "while kill -0 $child; do true; done"
9338 * will never exit even if $child died, unless we do this
9339 * to reap the zombie and make kill detect that it's gone: */
9340 dowait(DOWAIT_NONBLOCK, NULL);
9341
Eric Andersenc470f442003-07-28 09:56:35 +00009342 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9343 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009344 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009345 if (i == EXEXIT)
9346 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009347 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009348 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009349 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009350 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009351 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009352 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009353 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009354 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009355 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009356 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009357 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009358 }
9359 break;
9360
9361 case CMDFUNCTION:
9362 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009363 /* See above for the rationale */
9364 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009365 if (evalfun(cmdentry.u.func, argc, argv, flags))
9366 goto raise;
9367 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009368
9369 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009370
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009371 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009372 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009373 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009374 /* dsl: I think this is intended to be used to support
9375 * '_' in 'vi' command mode during line editing...
9376 * However I implemented that within libedit itself.
9377 */
9378 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009379 }
Eric Andersenc470f442003-07-28 09:56:35 +00009380 popstackmark(&smark);
9381}
9382
9383static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009384evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9385{
Eric Andersenc470f442003-07-28 09:56:35 +00009386 char *volatile savecmdname;
9387 struct jmploc *volatile savehandler;
9388 struct jmploc jmploc;
9389 int i;
9390
9391 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009392 i = setjmp(jmploc.loc);
9393 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009394 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009395 savehandler = exception_handler;
9396 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009397 commandname = argv[0];
9398 argptr = argv + 1;
9399 optptr = NULL; /* initialize nextopt */
9400 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009401 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009402 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009403 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009404 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009405 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009406 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009407
9408 return i;
9409}
9410
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009411static int
9412goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009413{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009414 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009415}
9416
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009417
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009418/*
9419 * Search for a command. This is called before we fork so that the
9420 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009421 * the child. The check for "goodname" is an overly conservative
9422 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009423 */
Eric Andersenc470f442003-07-28 09:56:35 +00009424static void
9425prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009426{
9427 struct cmdentry entry;
9428
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009429 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9430 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009431}
9432
Eric Andersencb57d552001-06-28 07:25:16 +00009433
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009434/* ============ Builtin commands
9435 *
9436 * Builtin commands whose functions are closely tied to evaluation
9437 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009438 */
9439
9440/*
Eric Andersencb57d552001-06-28 07:25:16 +00009441 * Handle break and continue commands. Break, continue, and return are
9442 * all handled by setting the evalskip flag. The evaluation routines
9443 * above all check this flag, and if it is set they start skipping
9444 * commands rather than executing them. The variable skipcount is
9445 * the number of loops to break/continue, or the number of function
9446 * levels to return. (The latter is always 1.) It should probably
9447 * be an error to break out of more loops than exist, but it isn't
9448 * in the standard shell so we don't make it one here.
9449 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009450static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009451breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009452{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009453 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009454
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009455 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009456 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009457 if (n > loopnest)
9458 n = loopnest;
9459 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009460 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009461 skipcount = n;
9462 }
9463 return 0;
9464}
9465
Eric Andersenc470f442003-07-28 09:56:35 +00009466
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009467/* ============ input.c
9468 *
Eric Andersen90898442003-08-06 11:20:52 +00009469 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009470 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009471
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009472enum {
9473 INPUT_PUSH_FILE = 1,
9474 INPUT_NOFILE_OK = 2,
9475};
Eric Andersencb57d552001-06-28 07:25:16 +00009476
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009477static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009478/* values of checkkwd variable */
9479#define CHKALIAS 0x1
9480#define CHKKWD 0x2
9481#define CHKNL 0x4
9482
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009483/*
9484 * Push a string back onto the input at this current parsefile level.
9485 * We handle aliases this way.
9486 */
9487#if !ENABLE_ASH_ALIAS
9488#define pushstring(s, ap) pushstring(s)
9489#endif
9490static void
9491pushstring(char *s, struct alias *ap)
9492{
9493 struct strpush *sp;
9494 int len;
9495
9496 len = strlen(s);
9497 INT_OFF;
9498 if (g_parsefile->strpush) {
9499 sp = ckzalloc(sizeof(*sp));
9500 sp->prev = g_parsefile->strpush;
9501 } else {
9502 sp = &(g_parsefile->basestrpush);
9503 }
9504 g_parsefile->strpush = sp;
9505 sp->prev_string = g_parsefile->next_to_pgetc;
9506 sp->prev_left_in_line = g_parsefile->left_in_line;
9507#if ENABLE_ASH_ALIAS
9508 sp->ap = ap;
9509 if (ap) {
9510 ap->flag |= ALIASINUSE;
9511 sp->string = s;
9512 }
9513#endif
9514 g_parsefile->next_to_pgetc = s;
9515 g_parsefile->left_in_line = len;
9516 INT_ON;
9517}
9518
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009519static void
9520popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009521{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009522 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009523
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009524 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009525#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009526 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009527 if (g_parsefile->next_to_pgetc[-1] == ' '
9528 || g_parsefile->next_to_pgetc[-1] == '\t'
9529 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009530 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009531 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009532 if (sp->string != sp->ap->val) {
9533 free(sp->string);
9534 }
9535 sp->ap->flag &= ~ALIASINUSE;
9536 if (sp->ap->flag & ALIASDEAD) {
9537 unalias(sp->ap->name);
9538 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009539 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009540#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009541 g_parsefile->next_to_pgetc = sp->prev_string;
9542 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009543 g_parsefile->strpush = sp->prev;
9544 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009545 free(sp);
9546 INT_ON;
9547}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009548
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009549//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9550//it peeks whether it is &>, and then pushes back both chars.
9551//This function needs to save last *next_to_pgetc to buf[0]
9552//to make two pungetc() reliable. Currently,
9553// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009554static int
9555preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009556{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009557 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009558 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009559
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009560 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009561#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009562 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009563 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9564 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009565 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009566#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009567 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009568#endif
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02009569 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009570 if (nr == 0) {
9571 /* Ctrl+C pressed */
9572 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009573 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009574 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009575 raise(SIGINT);
9576 return 1;
9577 }
Eric Andersenc470f442003-07-28 09:56:35 +00009578 goto retry;
9579 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009580 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009581 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009582 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009583 }
Eric Andersencb57d552001-06-28 07:25:16 +00009584 }
9585#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009586 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009587#endif
9588
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009589#if 0
9590/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009591 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009592 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009593 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009594 if (flags >= 0 && (flags & O_NONBLOCK)) {
9595 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009596 if (fcntl(0, F_SETFL, flags) >= 0) {
9597 out2str("sh: turning off NDELAY mode\n");
9598 goto retry;
9599 }
9600 }
9601 }
9602 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009603#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009604 return nr;
9605}
9606
9607/*
9608 * Refill the input buffer and return the next input character:
9609 *
9610 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009611 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9612 * or we are reading from a string so we can't refill the buffer,
9613 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009614 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009615 * 4) Process input up to the next newline, deleting nul characters.
9616 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009617//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9618#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009619static int
Eric Andersenc470f442003-07-28 09:56:35 +00009620preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009621{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009622 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009623 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009624
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009625 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009626#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009627 if (g_parsefile->left_in_line == -1
9628 && g_parsefile->strpush->ap
9629 && g_parsefile->next_to_pgetc[-1] != ' '
9630 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009631 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009632 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009633 return PEOA;
9634 }
Eric Andersen2870d962001-07-02 17:27:21 +00009635#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009636 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009637 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009638 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9639 g_parsefile->left_in_line,
9640 g_parsefile->next_to_pgetc,
9641 g_parsefile->next_to_pgetc);
9642 if (--g_parsefile->left_in_line >= 0)
9643 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009644 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009645 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009646 * "pgetc" needs refilling.
9647 */
9648
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009649 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009650 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009651 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009652 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009653 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009654 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009655 /* even in failure keep left_in_line and next_to_pgetc
9656 * in lock step, for correct multi-layer pungetc.
9657 * left_in_line was decremented before preadbuffer(),
9658 * must inc next_to_pgetc: */
9659 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009660 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009661 }
Eric Andersencb57d552001-06-28 07:25:16 +00009662
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009663 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009664 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009665 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009666 again:
9667 more = preadfd();
9668 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009669 /* don't try reading again */
9670 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009671 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009672 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009673 return PEOF;
9674 }
9675 }
9676
Denis Vlasenko727752d2008-11-28 03:41:47 +00009677 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009678 * Set g_parsefile->left_in_line
9679 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009680 * NUL chars are deleted.
9681 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009682 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009683 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009684 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009685
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009686 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009687
Denis Vlasenko727752d2008-11-28 03:41:47 +00009688 c = *q;
9689 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009690 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009691 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009692 q++;
9693 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009694 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009695 break;
9696 }
Eric Andersencb57d552001-06-28 07:25:16 +00009697 }
9698
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009699 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009700 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9701 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009702 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009703 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009704 }
9705 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009706 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009707
Eric Andersencb57d552001-06-28 07:25:16 +00009708 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009709 char save = *q;
9710 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009711 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009712 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009713 }
9714
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009715 pgetc_debug("preadbuffer at %d:%p'%s'",
9716 g_parsefile->left_in_line,
9717 g_parsefile->next_to_pgetc,
9718 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009719 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009720}
9721
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009722#define pgetc_as_macro() \
9723 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009724 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009725 : preadbuffer() \
9726 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009727
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009728static int
9729pgetc(void)
9730{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009731 pgetc_debug("pgetc_fast at %d:%p'%s'",
9732 g_parsefile->left_in_line,
9733 g_parsefile->next_to_pgetc,
9734 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009735 return pgetc_as_macro();
9736}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009737
9738#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009739# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009740#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009741# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009742#endif
9743
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009744#if ENABLE_ASH_ALIAS
9745static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009746pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009747{
9748 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009749 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009750 pgetc_debug("pgetc_fast at %d:%p'%s'",
9751 g_parsefile->left_in_line,
9752 g_parsefile->next_to_pgetc,
9753 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009754 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009755 } while (c == PEOA);
9756 return c;
9757}
9758#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009759# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009760#endif
9761
9762/*
9763 * Read a line from the script.
9764 */
9765static char *
9766pfgets(char *line, int len)
9767{
9768 char *p = line;
9769 int nleft = len;
9770 int c;
9771
9772 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009773 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009774 if (c == PEOF) {
9775 if (p == line)
9776 return NULL;
9777 break;
9778 }
9779 *p++ = c;
9780 if (c == '\n')
9781 break;
9782 }
9783 *p = '\0';
9784 return line;
9785}
9786
Eric Andersenc470f442003-07-28 09:56:35 +00009787/*
9788 * Undo the last call to pgetc. Only one character may be pushed back.
9789 * PEOF may be pushed back.
9790 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009791static void
Eric Andersenc470f442003-07-28 09:56:35 +00009792pungetc(void)
9793{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009794 g_parsefile->left_in_line++;
9795 g_parsefile->next_to_pgetc--;
9796 pgetc_debug("pushed back to %d:%p'%s'",
9797 g_parsefile->left_in_line,
9798 g_parsefile->next_to_pgetc,
9799 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009800}
9801
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009802/*
9803 * To handle the "." command, a stack of input files is used. Pushfile
9804 * adds a new entry to the stack and popfile restores the previous level.
9805 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009806static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009807pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009808{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009809 struct parsefile *pf;
9810
Denis Vlasenko597906c2008-02-20 16:38:54 +00009811 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009812 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009813 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009814 /*pf->strpush = NULL; - ckzalloc did it */
9815 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009816 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009817}
9818
9819static void
9820popfile(void)
9821{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009822 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009823
Denis Vlasenkob012b102007-02-19 22:43:01 +00009824 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009825 if (pf->pf_fd >= 0)
9826 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009827 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009828 while (pf->strpush)
9829 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009830 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009831 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009832 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009833}
9834
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009835/*
9836 * Return to top level.
9837 */
9838static void
9839popallfiles(void)
9840{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009841 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009842 popfile();
9843}
9844
9845/*
9846 * Close the file(s) that the shell is reading commands from. Called
9847 * after a fork is done.
9848 */
9849static void
9850closescript(void)
9851{
9852 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009853 if (g_parsefile->pf_fd > 0) {
9854 close(g_parsefile->pf_fd);
9855 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009856 }
9857}
9858
9859/*
9860 * Like setinputfile, but takes an open file descriptor. Call this with
9861 * interrupts off.
9862 */
9863static void
9864setinputfd(int fd, int push)
9865{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009866 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009867 if (push) {
9868 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009869 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009870 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009871 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009872 if (g_parsefile->buf == NULL)
9873 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009874 g_parsefile->left_in_buffer = 0;
9875 g_parsefile->left_in_line = 0;
9876 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009877}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009878
Eric Andersenc470f442003-07-28 09:56:35 +00009879/*
9880 * Set the input to take input from a file. If push is set, push the
9881 * old input onto the stack first.
9882 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009883static int
9884setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009885{
9886 int fd;
9887 int fd2;
9888
Denis Vlasenkob012b102007-02-19 22:43:01 +00009889 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009890 fd = open(fname, O_RDONLY);
9891 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009892 if (flags & INPUT_NOFILE_OK)
9893 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009894 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009895 }
Eric Andersenc470f442003-07-28 09:56:35 +00009896 if (fd < 10) {
9897 fd2 = copyfd(fd, 10);
9898 close(fd);
9899 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009900 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009901 fd = fd2;
9902 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009903 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009904 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009905 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009906 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009907}
9908
Eric Andersencb57d552001-06-28 07:25:16 +00009909/*
9910 * Like setinputfile, but takes input from a string.
9911 */
Eric Andersenc470f442003-07-28 09:56:35 +00009912static void
9913setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009914{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009915 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009916 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009917 g_parsefile->next_to_pgetc = string;
9918 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009919 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009920 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009921 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009922}
9923
9924
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009925/* ============ mail.c
9926 *
9927 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009928 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009929
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009930#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009931
Eric Andersencb57d552001-06-28 07:25:16 +00009932#define MAXMBOXES 10
9933
Eric Andersenc470f442003-07-28 09:56:35 +00009934/* times of mailboxes */
9935static time_t mailtime[MAXMBOXES];
9936/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009937static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009938
Eric Andersencb57d552001-06-28 07:25:16 +00009939/*
Eric Andersenc470f442003-07-28 09:56:35 +00009940 * Print appropriate message(s) if mail has arrived.
9941 * If mail_var_path_changed is set,
9942 * then the value of MAIL has mail_var_path_changed,
9943 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009944 */
Eric Andersenc470f442003-07-28 09:56:35 +00009945static void
9946chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009947{
Eric Andersencb57d552001-06-28 07:25:16 +00009948 const char *mpath;
9949 char *p;
9950 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009951 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009952 struct stackmark smark;
9953 struct stat statb;
9954
Eric Andersencb57d552001-06-28 07:25:16 +00009955 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009956 mpath = mpathset() ? mpathval() : mailval();
9957 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02009958 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00009959 if (p == NULL)
9960 break;
9961 if (*p == '\0')
9962 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009963 for (q = p; *q; q++)
9964 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009965#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009966 if (q[-1] != '/')
9967 abort();
9968#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009969 q[-1] = '\0'; /* delete trailing '/' */
9970 if (stat(p, &statb) < 0) {
9971 *mtp = 0;
9972 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009973 }
Eric Andersenc470f442003-07-28 09:56:35 +00009974 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9975 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02009976 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +00009977 pathopt ? pathopt : "you have mail"
9978 );
9979 }
9980 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009981 }
Eric Andersenc470f442003-07-28 09:56:35 +00009982 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009983 popstackmark(&smark);
9984}
Eric Andersencb57d552001-06-28 07:25:16 +00009985
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009986static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009987changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009988{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009989 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009990}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009991
Denis Vlasenko131ae172007-02-18 13:00:19 +00009992#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009993
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009994
9995/* ============ ??? */
9996
Eric Andersencb57d552001-06-28 07:25:16 +00009997/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009998 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009999 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010000static void
10001setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010002{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010003 char **newparam;
10004 char **ap;
10005 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010006
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010007 for (nparam = 0; argv[nparam]; nparam++)
10008 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010009 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10010 while (*argv) {
10011 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010012 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010013 *ap = NULL;
10014 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010015 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010016 shellparam.nparam = nparam;
10017 shellparam.p = newparam;
10018#if ENABLE_ASH_GETOPTS
10019 shellparam.optind = 1;
10020 shellparam.optoff = -1;
10021#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010022}
10023
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010024/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010025 * Process shell options. The global variable argptr contains a pointer
10026 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010027 *
10028 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10029 * For a non-interactive shell, an error condition encountered
10030 * by a special built-in ... shall cause the shell to write a diagnostic message
10031 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010032 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010033 * ...
10034 * Utility syntax error (option or operand error) Shall exit
10035 * ...
10036 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10037 * we see that bash does not do that (set "finishes" with error code 1 instead,
10038 * and shell continues), and people rely on this behavior!
10039 * Testcase:
10040 * set -o barfoo 2>/dev/null
10041 * echo $?
10042 *
10043 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010044 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010045static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010046plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010047{
10048 int i;
10049
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010050 if (name) {
10051 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010052 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010053 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010054 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010055 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010056 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010057 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010058 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010059 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010060 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010061 if (val) {
10062 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10063 } else {
10064 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10065 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010066 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010067 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010068}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010069static void
10070setoption(int flag, int val)
10071{
10072 int i;
10073
10074 for (i = 0; i < NOPTS; i++) {
10075 if (optletters(i) == flag) {
10076 optlist[i] = val;
10077 return;
10078 }
10079 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010080 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010081 /* NOTREACHED */
10082}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010083static int
Eric Andersenc470f442003-07-28 09:56:35 +000010084options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010085{
10086 char *p;
10087 int val;
10088 int c;
10089
10090 if (cmdline)
10091 minusc = NULL;
10092 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010093 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010094 if (c != '-' && c != '+')
10095 break;
10096 argptr++;
10097 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010098 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010099 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010100 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010101 if (!cmdline) {
10102 /* "-" means turn off -x and -v */
10103 if (p[0] == '\0')
10104 xflag = vflag = 0;
10105 /* "--" means reset params */
10106 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010107 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010108 }
Eric Andersenc470f442003-07-28 09:56:35 +000010109 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010110 }
Eric Andersencb57d552001-06-28 07:25:16 +000010111 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010112 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010113 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010114 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010115 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010116 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010117 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010118 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010119 /* it already printed err message */
10120 return 1; /* error */
10121 }
Eric Andersencb57d552001-06-28 07:25:16 +000010122 if (*argptr)
10123 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010124 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10125 isloginsh = 1;
10126 /* bash does not accept +-login, we also won't */
10127 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010128 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010129 isloginsh = 1;
10130 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010131 } else {
10132 setoption(c, val);
10133 }
10134 }
10135 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010136 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010137}
10138
Eric Andersencb57d552001-06-28 07:25:16 +000010139/*
Eric Andersencb57d552001-06-28 07:25:16 +000010140 * The shift builtin command.
10141 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010142static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010143shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010144{
10145 int n;
10146 char **ap1, **ap2;
10147
10148 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010149 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010150 n = number(argv[1]);
10151 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010152 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010153 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010154 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010155 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010156 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010157 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010158 }
10159 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010160 while ((*ap2++ = *ap1++) != NULL)
10161 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010162#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010163 shellparam.optind = 1;
10164 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010165#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010166 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010167 return 0;
10168}
10169
Eric Andersencb57d552001-06-28 07:25:16 +000010170/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010171 * POSIX requires that 'set' (but not export or readonly) output the
10172 * variables in lexicographic order - by the locale's collating order (sigh).
10173 * Maybe we could keep them in an ordered balanced binary tree
10174 * instead of hashed lists.
10175 * For now just roll 'em through qsort for printing...
10176 */
10177static int
10178showvars(const char *sep_prefix, int on, int off)
10179{
10180 const char *sep;
10181 char **ep, **epend;
10182
10183 ep = listvars(on, off, &epend);
10184 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10185
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010186 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010187
10188 for (; ep < epend; ep++) {
10189 const char *p;
10190 const char *q;
10191
10192 p = strchrnul(*ep, '=');
10193 q = nullstr;
10194 if (*p)
10195 q = single_quote(++p);
10196 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10197 }
10198 return 0;
10199}
10200
10201/*
Eric Andersencb57d552001-06-28 07:25:16 +000010202 * The set command builtin.
10203 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010204static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010205setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010206{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010207 int retval;
10208
Denis Vlasenko68404f12008-03-17 09:00:54 +000010209 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010210 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010211 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010212 retval = 1;
10213 if (!options(0)) { /* if no parse error... */
10214 retval = 0;
10215 optschanged();
10216 if (*argptr != NULL) {
10217 setparam(argptr);
10218 }
Eric Andersencb57d552001-06-28 07:25:16 +000010219 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010220 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010221 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010222}
10223
Denis Vlasenko131ae172007-02-18 13:00:19 +000010224#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010225static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010226change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010227{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010228 uint32_t t;
10229
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010230 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010231 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010232 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010233 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010234 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010235 vrandom.flags &= ~VNOFUNC;
10236 } else {
10237 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010238 t = strtoul(value, NULL, 10);
10239 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010240 }
Eric Andersenef02f822004-03-11 13:34:24 +000010241}
Eric Andersen16767e22004-03-16 05:14:10 +000010242#endif
10243
Denis Vlasenko131ae172007-02-18 13:00:19 +000010244#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010245static int
Eric Andersenc470f442003-07-28 09:56:35 +000010246getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010247{
10248 char *p, *q;
10249 char c = '?';
10250 int done = 0;
10251 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010252 char s[12];
10253 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010254
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010255 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010256 return 1;
10257 optnext = optfirst + *param_optind - 1;
10258
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010259 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010260 p = NULL;
10261 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010262 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010263 if (p == NULL || *p == '\0') {
10264 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010265 p = *optnext;
10266 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010267 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010268 p = NULL;
10269 done = 1;
10270 goto out;
10271 }
10272 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010273 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010274 goto atend;
10275 }
10276
10277 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010278 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010279 if (*q == '\0') {
10280 if (optstr[0] == ':') {
10281 s[0] = c;
10282 s[1] = '\0';
10283 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010284 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010285 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010286 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010287 }
10288 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010289 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010290 }
10291 if (*++q == ':')
10292 q++;
10293 }
10294
10295 if (*++q == ':') {
10296 if (*p == '\0' && (p = *optnext) == NULL) {
10297 if (optstr[0] == ':') {
10298 s[0] = c;
10299 s[1] = '\0';
10300 err |= setvarsafe("OPTARG", s, 0);
10301 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010302 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010303 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010304 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010305 c = '?';
10306 }
Eric Andersenc470f442003-07-28 09:56:35 +000010307 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010308 }
10309
10310 if (p == *optnext)
10311 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010312 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010313 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010314 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010315 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010316 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010317 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010318 *param_optind = optnext - optfirst + 1;
10319 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010320 err |= setvarsafe("OPTIND", s, VNOFUNC);
10321 s[0] = c;
10322 s[1] = '\0';
10323 err |= setvarsafe(optvar, s, 0);
10324 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010325 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010326 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010327 flush_stdout_stderr();
10328 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010329 }
10330 return done;
10331}
Eric Andersenc470f442003-07-28 09:56:35 +000010332
10333/*
10334 * The getopts builtin. Shellparam.optnext points to the next argument
10335 * to be processed. Shellparam.optptr points to the next character to
10336 * be processed in the current argument. If shellparam.optnext is NULL,
10337 * then it's the first time getopts has been called.
10338 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010339static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010340getoptscmd(int argc, char **argv)
10341{
10342 char **optbase;
10343
10344 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010345 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010346 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010347 optbase = shellparam.p;
10348 if (shellparam.optind > shellparam.nparam + 1) {
10349 shellparam.optind = 1;
10350 shellparam.optoff = -1;
10351 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010352 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010353 optbase = &argv[3];
10354 if (shellparam.optind > argc - 2) {
10355 shellparam.optind = 1;
10356 shellparam.optoff = -1;
10357 }
10358 }
10359
10360 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010361 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010362}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010363#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010364
Eric Andersencb57d552001-06-28 07:25:16 +000010365
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010366/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010367
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010368struct heredoc {
10369 struct heredoc *next; /* next here document in list */
10370 union node *here; /* redirection node */
10371 char *eofmark; /* string indicating end of input */
10372 smallint striptabs; /* if set, strip leading tabs */
10373};
10374
10375static smallint tokpushback; /* last token pushed back */
10376static smallint parsebackquote; /* nonzero if we are inside backquotes */
10377static smallint quoteflag; /* set if (part of) last token was quoted */
10378static token_id_t lasttoken; /* last token read (integer id Txxx) */
10379static struct heredoc *heredoclist; /* list of here documents to read */
10380static char *wordtext; /* text of last word returned by readtoken */
10381static struct nodelist *backquotelist;
10382static union node *redirnode;
10383static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010384
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010385static const char *
10386tokname(char *buf, int tok)
10387{
10388 if (tok < TSEMI)
10389 return tokname_array[tok] + 1;
10390 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10391 return buf;
10392}
10393
10394/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010395 * Called when an unexpected token is read during the parse. The argument
10396 * is the token that is expected, or -1 if more than one type of token can
10397 * occur at this point.
10398 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010399static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010400static void
10401raise_error_unexpected_syntax(int token)
10402{
10403 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010404 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010405 int l;
10406
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010407 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010408 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010409 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010410 raise_error_syntax(msg);
10411 /* NOTREACHED */
10412}
Eric Andersencb57d552001-06-28 07:25:16 +000010413
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010414#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010415
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010416/* parsing is heavily cross-recursive, need these forward decls */
10417static union node *andor(void);
10418static union node *pipeline(void);
10419static union node *parse_command(void);
10420static void parseheredoc(void);
10421static char peektoken(void);
10422static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010423
Eric Andersenc470f442003-07-28 09:56:35 +000010424static union node *
10425list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010426{
10427 union node *n1, *n2, *n3;
10428 int tok;
10429
Eric Andersenc470f442003-07-28 09:56:35 +000010430 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10431 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010432 return NULL;
10433 n1 = NULL;
10434 for (;;) {
10435 n2 = andor();
10436 tok = readtoken();
10437 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010438 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010439 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010440 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010441 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010442 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010443 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010444 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010445 n2 = n3;
10446 }
10447 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010448 }
10449 }
10450 if (n1 == NULL) {
10451 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010452 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010453 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010454 n3->type = NSEMI;
10455 n3->nbinary.ch1 = n1;
10456 n3->nbinary.ch2 = n2;
10457 n1 = n3;
10458 }
10459 switch (tok) {
10460 case TBACKGND:
10461 case TSEMI:
10462 tok = readtoken();
10463 /* fall through */
10464 case TNL:
10465 if (tok == TNL) {
10466 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010467 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010468 return n1;
10469 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010470 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010471 }
Eric Andersenc470f442003-07-28 09:56:35 +000010472 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010473 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010474 return n1;
10475 break;
10476 case TEOF:
10477 if (heredoclist)
10478 parseheredoc();
10479 else
Eric Andersenc470f442003-07-28 09:56:35 +000010480 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010481 return n1;
10482 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010483 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010484 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010485 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010486 return n1;
10487 }
10488 }
10489}
10490
Eric Andersenc470f442003-07-28 09:56:35 +000010491static union node *
10492andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010493{
Eric Andersencb57d552001-06-28 07:25:16 +000010494 union node *n1, *n2, *n3;
10495 int t;
10496
Eric Andersencb57d552001-06-28 07:25:16 +000010497 n1 = pipeline();
10498 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010499 t = readtoken();
10500 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010501 t = NAND;
10502 } else if (t == TOR) {
10503 t = NOR;
10504 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010505 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010506 return n1;
10507 }
Eric Andersenc470f442003-07-28 09:56:35 +000010508 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010509 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010510 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010511 n3->type = t;
10512 n3->nbinary.ch1 = n1;
10513 n3->nbinary.ch2 = n2;
10514 n1 = n3;
10515 }
10516}
10517
Eric Andersenc470f442003-07-28 09:56:35 +000010518static union node *
10519pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010520{
Eric Andersencb57d552001-06-28 07:25:16 +000010521 union node *n1, *n2, *pipenode;
10522 struct nodelist *lp, *prev;
10523 int negate;
10524
10525 negate = 0;
10526 TRACE(("pipeline: entered\n"));
10527 if (readtoken() == TNOT) {
10528 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010529 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010530 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010531 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010532 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010533 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010534 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010535 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010536 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010537 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010538 pipenode->npipe.cmdlist = lp;
10539 lp->n = n1;
10540 do {
10541 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010542 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010543 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010544 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010545 prev->next = lp;
10546 } while (readtoken() == TPIPE);
10547 lp->next = NULL;
10548 n1 = pipenode;
10549 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010550 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010551 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010552 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010553 n2->type = NNOT;
10554 n2->nnot.com = n1;
10555 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010556 }
10557 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010558}
10559
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010560static union node *
10561makename(void)
10562{
10563 union node *n;
10564
Denis Vlasenko597906c2008-02-20 16:38:54 +000010565 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010566 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010567 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010568 n->narg.text = wordtext;
10569 n->narg.backquote = backquotelist;
10570 return n;
10571}
10572
10573static void
10574fixredir(union node *n, const char *text, int err)
10575{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010576 int fd;
10577
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010578 TRACE(("Fix redir %s %d\n", text, err));
10579 if (!err)
10580 n->ndup.vname = NULL;
10581
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010582 fd = bb_strtou(text, NULL, 10);
10583 if (!errno && fd >= 0)
10584 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010585 else if (LONE_DASH(text))
10586 n->ndup.dupfd = -1;
10587 else {
10588 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010589 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010590 n->ndup.vname = makename();
10591 }
10592}
10593
10594/*
10595 * Returns true if the text contains nothing to expand (no dollar signs
10596 * or backquotes).
10597 */
10598static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010599noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010600{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010601 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010602
Denys Vlasenkocd716832009-11-28 22:14:02 +010010603 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010604 if (c == CTLQUOTEMARK)
10605 continue;
10606 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010607 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010608 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010609 return 0;
10610 }
10611 return 1;
10612}
10613
10614static void
10615parsefname(void)
10616{
10617 union node *n = redirnode;
10618
10619 if (readtoken() != TWORD)
10620 raise_error_unexpected_syntax(-1);
10621 if (n->type == NHERE) {
10622 struct heredoc *here = heredoc;
10623 struct heredoc *p;
10624 int i;
10625
10626 if (quoteflag == 0)
10627 n->type = NXHERE;
10628 TRACE(("Here document %d\n", n->type));
10629 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010630 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010631 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010632 here->eofmark = wordtext;
10633 here->next = NULL;
10634 if (heredoclist == NULL)
10635 heredoclist = here;
10636 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010637 for (p = heredoclist; p->next; p = p->next)
10638 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010639 p->next = here;
10640 }
10641 } else if (n->type == NTOFD || n->type == NFROMFD) {
10642 fixredir(n, wordtext, 0);
10643 } else {
10644 n->nfile.fname = makename();
10645 }
10646}
Eric Andersencb57d552001-06-28 07:25:16 +000010647
Eric Andersenc470f442003-07-28 09:56:35 +000010648static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010649simplecmd(void)
10650{
10651 union node *args, **app;
10652 union node *n = NULL;
10653 union node *vars, **vpp;
10654 union node **rpp, *redir;
10655 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010656#if ENABLE_ASH_BASH_COMPAT
10657 smallint double_brackets_flag = 0;
10658#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010659
10660 args = NULL;
10661 app = &args;
10662 vars = NULL;
10663 vpp = &vars;
10664 redir = NULL;
10665 rpp = &redir;
10666
10667 savecheckkwd = CHKALIAS;
10668 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010669 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010670 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010671 t = readtoken();
10672 switch (t) {
10673#if ENABLE_ASH_BASH_COMPAT
10674 case TAND: /* "&&" */
10675 case TOR: /* "||" */
10676 if (!double_brackets_flag) {
10677 tokpushback = 1;
10678 goto out;
10679 }
10680 wordtext = (char *) (t == TAND ? "-a" : "-o");
10681#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010682 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010683 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010684 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010685 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010686 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010687#if ENABLE_ASH_BASH_COMPAT
10688 if (strcmp("[[", wordtext) == 0)
10689 double_brackets_flag = 1;
10690 else if (strcmp("]]", wordtext) == 0)
10691 double_brackets_flag = 0;
10692#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010693 n->narg.backquote = backquotelist;
10694 if (savecheckkwd && isassignment(wordtext)) {
10695 *vpp = n;
10696 vpp = &n->narg.next;
10697 } else {
10698 *app = n;
10699 app = &n->narg.next;
10700 savecheckkwd = 0;
10701 }
10702 break;
10703 case TREDIR:
10704 *rpp = n = redirnode;
10705 rpp = &n->nfile.next;
10706 parsefname(); /* read name of redirection file */
10707 break;
10708 case TLP:
10709 if (args && app == &args->narg.next
10710 && !vars && !redir
10711 ) {
10712 struct builtincmd *bcmd;
10713 const char *name;
10714
10715 /* We have a function */
10716 if (readtoken() != TRP)
10717 raise_error_unexpected_syntax(TRP);
10718 name = n->narg.text;
10719 if (!goodname(name)
10720 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10721 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010722 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010723 }
10724 n->type = NDEFUN;
10725 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10726 n->narg.next = parse_command();
10727 return n;
10728 }
10729 /* fall through */
10730 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010731 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010732 goto out;
10733 }
10734 }
10735 out:
10736 *app = NULL;
10737 *vpp = NULL;
10738 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010739 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010740 n->type = NCMD;
10741 n->ncmd.args = args;
10742 n->ncmd.assign = vars;
10743 n->ncmd.redirect = redir;
10744 return n;
10745}
10746
10747static union node *
10748parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010749{
Eric Andersencb57d552001-06-28 07:25:16 +000010750 union node *n1, *n2;
10751 union node *ap, **app;
10752 union node *cp, **cpp;
10753 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010754 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010755 int t;
10756
10757 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010758 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010759
Eric Andersencb57d552001-06-28 07:25:16 +000010760 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010761 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010762 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010763 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010764 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010765 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010766 n1->type = NIF;
10767 n1->nif.test = list(0);
10768 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010769 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010770 n1->nif.ifpart = list(0);
10771 n2 = n1;
10772 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010773 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010774 n2 = n2->nif.elsepart;
10775 n2->type = NIF;
10776 n2->nif.test = list(0);
10777 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010778 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010779 n2->nif.ifpart = list(0);
10780 }
10781 if (lasttoken == TELSE)
10782 n2->nif.elsepart = list(0);
10783 else {
10784 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010785 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010786 }
Eric Andersenc470f442003-07-28 09:56:35 +000010787 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010788 break;
10789 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010790 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010791 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010792 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010793 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010794 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010795 got = readtoken();
10796 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010797 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010798 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010799 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010800 }
10801 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010802 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010803 break;
10804 }
10805 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010806 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010807 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010808 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010809 n1->type = NFOR;
10810 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010811 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010812 if (readtoken() == TIN) {
10813 app = &ap;
10814 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010815 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010816 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010817 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010818 n2->narg.text = wordtext;
10819 n2->narg.backquote = backquotelist;
10820 *app = n2;
10821 app = &n2->narg.next;
10822 }
10823 *app = NULL;
10824 n1->nfor.args = ap;
10825 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010826 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010827 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010828 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010829 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010830 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010831 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010832 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010833 n1->nfor.args = n2;
10834 /*
10835 * Newline or semicolon here is optional (but note
10836 * that the original Bourne shell only allowed NL).
10837 */
10838 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010839 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010840 }
Eric Andersenc470f442003-07-28 09:56:35 +000010841 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010842 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010843 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010844 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010845 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010846 break;
10847 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010848 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010849 n1->type = NCASE;
10850 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010851 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010852 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010853 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010854 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010855 n2->narg.text = wordtext;
10856 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010857 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010858 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010859 } while (readtoken() == TNL);
10860 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010861 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010862 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010863 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010864 checkkwd = CHKNL | CHKKWD;
10865 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010866 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010867 if (lasttoken == TLP)
10868 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010869 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010870 cp->type = NCLIST;
10871 app = &cp->nclist.pattern;
10872 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010873 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010874 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010875 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010876 ap->narg.text = wordtext;
10877 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010878 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010879 break;
10880 app = &ap->narg.next;
10881 readtoken();
10882 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010883 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010884 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010885 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010886 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010887
Eric Andersenc470f442003-07-28 09:56:35 +000010888 cpp = &cp->nclist.next;
10889
10890 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010891 t = readtoken();
10892 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010893 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010894 raise_error_unexpected_syntax(TENDCASE);
10895 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010896 }
Eric Andersenc470f442003-07-28 09:56:35 +000010897 }
Eric Andersencb57d552001-06-28 07:25:16 +000010898 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010899 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010900 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010901 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010902 n1->type = NSUBSHELL;
10903 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010904 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010905 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010906 break;
10907 case TBEGIN:
10908 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010909 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010910 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010911 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010912 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010913 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010914 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010915 }
10916
Eric Andersenc470f442003-07-28 09:56:35 +000010917 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010918 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010919
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010920 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010921 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010922 checkkwd = CHKKWD | CHKALIAS;
10923 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010924 while (readtoken() == TREDIR) {
10925 *rpp = n2 = redirnode;
10926 rpp = &n2->nfile.next;
10927 parsefname();
10928 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010929 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010930 *rpp = NULL;
10931 if (redir) {
10932 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010933 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010934 n2->type = NREDIR;
10935 n2->nredir.n = n1;
10936 n1 = n2;
10937 }
10938 n1->nredir.redirect = redir;
10939 }
Eric Andersencb57d552001-06-28 07:25:16 +000010940 return n1;
10941}
10942
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010943#if ENABLE_ASH_BASH_COMPAT
10944static int decode_dollar_squote(void)
10945{
10946 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10947 int c, cnt;
10948 char *p;
10949 char buf[4];
10950
10951 c = pgetc();
10952 p = strchr(C_escapes, c);
10953 if (p) {
10954 buf[0] = c;
10955 p = buf;
10956 cnt = 3;
10957 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10958 do {
10959 c = pgetc();
10960 *++p = c;
10961 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10962 pungetc();
10963 } else if (c == 'x') { /* \xHH */
10964 do {
10965 c = pgetc();
10966 *++p = c;
10967 } while (isxdigit(c) && --cnt);
10968 pungetc();
10969 if (cnt == 3) { /* \x but next char is "bad" */
10970 c = 'x';
10971 goto unrecognized;
10972 }
10973 } else { /* simple seq like \\ or \t */
10974 p++;
10975 }
10976 *p = '\0';
10977 p = buf;
10978 c = bb_process_escape_sequence((void*)&p);
10979 } else { /* unrecognized "\z": print both chars unless ' or " */
10980 if (c != '\'' && c != '"') {
10981 unrecognized:
10982 c |= 0x100; /* "please encode \, then me" */
10983 }
10984 }
10985 return c;
10986}
10987#endif
10988
Eric Andersencb57d552001-06-28 07:25:16 +000010989/*
10990 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10991 * is not NULL, read a here document. In the latter case, eofmark is the
10992 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010010993 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000010994 * is the first character of the input token or document.
10995 *
10996 * Because C does not have internal subroutines, I have simulated them
10997 * using goto's to implement the subroutine linkage. The following macros
10998 * will run code that appears at the end of readtoken1.
10999 */
Eric Andersen2870d962001-07-02 17:27:21 +000011000#define CHECKEND() {goto checkend; checkend_return:;}
11001#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11002#define PARSESUB() {goto parsesub; parsesub_return:;}
11003#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11004#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11005#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011006static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011007readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011008{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011009 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011010 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011011 char *out;
11012 int len;
11013 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011014 struct nodelist *bqlist;
11015 smallint quotef;
11016 smallint dblquote;
11017 smallint oldstyle;
11018 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011019#if ENABLE_ASH_EXPAND_PRMT
11020 smallint pssyntax; /* we are expanding a prompt string */
11021#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011022 int varnest; /* levels of variables expansion */
11023 int arinest; /* levels of arithmetic expansion */
11024 int parenlevel; /* levels of parens in arithmetic */
11025 int dqvarnest; /* levels of variables expansion within double quotes */
11026
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011027 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011028
Eric Andersencb57d552001-06-28 07:25:16 +000011029#if __GNUC__
11030 /* Avoid longjmp clobbering */
11031 (void) &out;
11032 (void) &quotef;
11033 (void) &dblquote;
11034 (void) &varnest;
11035 (void) &arinest;
11036 (void) &parenlevel;
11037 (void) &dqvarnest;
11038 (void) &oldstyle;
11039 (void) &prevsyntax;
11040 (void) &syntax;
11041#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011042 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011043 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011044 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011045 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011046#if ENABLE_ASH_EXPAND_PRMT
11047 pssyntax = (syntax == PSSYNTAX);
11048 if (pssyntax)
11049 syntax = DQSYNTAX;
11050#endif
11051 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011052 varnest = 0;
11053 arinest = 0;
11054 parenlevel = 0;
11055 dqvarnest = 0;
11056
11057 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011058 loop:
11059 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011060 CHECKEND(); /* set c to PEOF if at end of here document */
11061 for (;;) { /* until end of line or end of word */
11062 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11063 switch (SIT(c, syntax)) {
11064 case CNL: /* '\n' */
11065 if (syntax == BASESYNTAX)
11066 goto endword; /* exit outer loop */
11067 USTPUTC(c, out);
11068 g_parsefile->linno++;
11069 setprompt_if(doprompt, 2);
11070 c = pgetc();
11071 goto loop; /* continue outer loop */
11072 case CWORD:
11073 USTPUTC(c, out);
11074 break;
11075 case CCTL:
11076 if (eofmark == NULL || dblquote)
11077 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011078#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011079 if (c == '\\' && bash_dollar_squote) {
11080 c = decode_dollar_squote();
11081 if (c & 0x100) {
11082 USTPUTC('\\', out);
11083 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011084 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011085 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011086#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011087 USTPUTC(c, out);
11088 break;
11089 case CBACK: /* backslash */
11090 c = pgetc_without_PEOA();
11091 if (c == PEOF) {
11092 USTPUTC(CTLESC, out);
11093 USTPUTC('\\', out);
11094 pungetc();
11095 } else if (c == '\n') {
11096 setprompt_if(doprompt, 2);
11097 } else {
11098#if ENABLE_ASH_EXPAND_PRMT
11099 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011100 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011101 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011102 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011103#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011104 /* Backslash is retained if we are in "str" and next char isn't special */
11105 if (dblquote
11106 && c != '\\'
11107 && c != '`'
11108 && c != '$'
11109 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011110 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011111 USTPUTC(CTLESC, out);
11112 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011113 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011114 if (SIT(c, SQSYNTAX) == CCTL)
11115 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011116 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011117 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011118 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011119 break;
11120 case CSQUOTE:
11121 syntax = SQSYNTAX;
11122 quotemark:
11123 if (eofmark == NULL) {
11124 USTPUTC(CTLQUOTEMARK, out);
11125 }
11126 break;
11127 case CDQUOTE:
11128 syntax = DQSYNTAX;
11129 dblquote = 1;
11130 goto quotemark;
11131 case CENDQUOTE:
11132 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11133 if (eofmark != NULL && arinest == 0
11134 && varnest == 0
11135 ) {
11136 USTPUTC(c, out);
11137 } else {
11138 if (dqvarnest == 0) {
11139 syntax = BASESYNTAX;
11140 dblquote = 0;
11141 }
11142 quotef = 1;
11143 goto quotemark;
11144 }
11145 break;
11146 case CVAR: /* '$' */
11147 PARSESUB(); /* parse substitution */
11148 break;
11149 case CENDVAR: /* '}' */
11150 if (varnest > 0) {
11151 varnest--;
11152 if (dqvarnest > 0) {
11153 dqvarnest--;
11154 }
11155 c = CTLENDVAR;
11156 }
11157 USTPUTC(c, out);
11158 break;
11159#if ENABLE_SH_MATH_SUPPORT
11160 case CLP: /* '(' in arithmetic */
11161 parenlevel++;
11162 USTPUTC(c, out);
11163 break;
11164 case CRP: /* ')' in arithmetic */
11165 if (parenlevel > 0) {
11166 parenlevel--;
11167 } else {
11168 if (pgetc() == ')') {
11169 if (--arinest == 0) {
11170 syntax = prevsyntax;
11171 dblquote = (syntax == DQSYNTAX);
11172 c = CTLENDARI;
11173 }
11174 } else {
11175 /*
11176 * unbalanced parens
11177 * (don't 2nd guess - no error)
11178 */
11179 pungetc();
11180 }
11181 }
11182 USTPUTC(c, out);
11183 break;
11184#endif
11185 case CBQUOTE: /* '`' */
11186 PARSEBACKQOLD();
11187 break;
11188 case CENDFILE:
11189 goto endword; /* exit outer loop */
11190 case CIGN:
11191 break;
11192 default:
11193 if (varnest == 0) {
11194#if ENABLE_ASH_BASH_COMPAT
11195 if (c == '&') {
11196 if (pgetc() == '>')
11197 c = 0x100 + '>'; /* flag &> */
11198 pungetc();
11199 }
11200#endif
11201 goto endword; /* exit outer loop */
11202 }
11203 IF_ASH_ALIAS(if (c != PEOA))
11204 USTPUTC(c, out);
11205 }
11206 c = pgetc_fast();
11207 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011208 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011209
Mike Frysinger98c52642009-04-02 10:02:37 +000011210#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011211 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011212 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011213#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011214 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011215 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011216 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011217 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011218 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011219 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011220 }
11221 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011222 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011223 out = stackblock();
11224 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011225 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011226 && quotef == 0
11227 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011228 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011229 PARSEREDIR(); /* passed as params: out, c */
11230 lasttoken = TREDIR;
11231 return lasttoken;
11232 }
11233 /* else: non-number X seen, interpret it
11234 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011235 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011236 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011237 }
11238 quoteflag = quotef;
11239 backquotelist = bqlist;
11240 grabstackblock(len);
11241 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011242 lasttoken = TWORD;
11243 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011244/* end of readtoken routine */
11245
Eric Andersencb57d552001-06-28 07:25:16 +000011246/*
11247 * Check to see whether we are at the end of the here document. When this
11248 * is called, c is set to the first character of the next input line. If
11249 * we are at the end of the here document, this routine sets the c to PEOF.
11250 */
Eric Andersenc470f442003-07-28 09:56:35 +000011251checkend: {
11252 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011253#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011254 if (c == PEOA)
11255 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011256#endif
11257 if (striptabs) {
11258 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011259 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011260 }
Eric Andersenc470f442003-07-28 09:56:35 +000011261 }
11262 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011263 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011264 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011265
Eric Andersenc470f442003-07-28 09:56:35 +000011266 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011267 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11268 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011269 if (*p == '\n' && *q == '\0') {
11270 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011271 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011272 needprompt = doprompt;
11273 } else {
11274 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011275 }
11276 }
11277 }
11278 }
Eric Andersenc470f442003-07-28 09:56:35 +000011279 goto checkend_return;
11280}
Eric Andersencb57d552001-06-28 07:25:16 +000011281
Eric Andersencb57d552001-06-28 07:25:16 +000011282/*
11283 * Parse a redirection operator. The variable "out" points to a string
11284 * specifying the fd to be redirected. The variable "c" contains the
11285 * first character of the redirection operator.
11286 */
Eric Andersenc470f442003-07-28 09:56:35 +000011287parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011288 /* out is already checked to be a valid number or "" */
11289 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011290 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011291
Denis Vlasenko597906c2008-02-20 16:38:54 +000011292 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011293 if (c == '>') {
11294 np->nfile.fd = 1;
11295 c = pgetc();
11296 if (c == '>')
11297 np->type = NAPPEND;
11298 else if (c == '|')
11299 np->type = NCLOBBER;
11300 else if (c == '&')
11301 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011302 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011303 else {
11304 np->type = NTO;
11305 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011306 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011307 }
11308#if ENABLE_ASH_BASH_COMPAT
11309 else if (c == 0x100 + '>') { /* this flags &> redirection */
11310 np->nfile.fd = 1;
11311 pgetc(); /* this is '>', no need to check */
11312 np->type = NTO2;
11313 }
11314#endif
11315 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011316 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011317 c = pgetc();
11318 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011319 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011320 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011321 np = stzalloc(sizeof(struct nhere));
11322 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011323 }
11324 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011325 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011326 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011327 c = pgetc();
11328 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011329 heredoc->striptabs = 1;
11330 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011331 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011332 pungetc();
11333 }
11334 break;
11335
11336 case '&':
11337 np->type = NFROMFD;
11338 break;
11339
11340 case '>':
11341 np->type = NFROMTO;
11342 break;
11343
11344 default:
11345 np->type = NFROM;
11346 pungetc();
11347 break;
11348 }
Eric Andersencb57d552001-06-28 07:25:16 +000011349 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011350 if (fd >= 0)
11351 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011352 redirnode = np;
11353 goto parseredir_return;
11354}
Eric Andersencb57d552001-06-28 07:25:16 +000011355
Eric Andersencb57d552001-06-28 07:25:16 +000011356/*
11357 * Parse a substitution. At this point, we have read the dollar sign
11358 * and nothing else.
11359 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011360
11361/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11362 * (assuming ascii char codes, as the original implementation did) */
11363#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011364 (((unsigned)(c) - 33 < 32) \
11365 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011366parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011367 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011368 int typeloc;
11369 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011370
Eric Andersenc470f442003-07-28 09:56:35 +000011371 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011372 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011373 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011374 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011375#if ENABLE_ASH_BASH_COMPAT
11376 if (c == '\'')
11377 bash_dollar_squote = 1;
11378 else
11379#endif
11380 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011381 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011382 } else if (c == '(') {
11383 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011384 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011385#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011386 PARSEARITH();
11387#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011388 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011389#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011390 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011391 pungetc();
11392 PARSEBACKQNEW();
11393 }
11394 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011395 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011396 USTPUTC(CTLVAR, out);
11397 typeloc = out - (char *)stackblock();
11398 USTPUTC(VSNORMAL, out);
11399 subtype = VSNORMAL;
11400 if (c == '{') {
11401 c = pgetc();
11402 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011403 c = pgetc();
11404 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011405 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011406 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011407 subtype = VSLENGTH; /* ${#VAR} */
11408 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011409 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011410 }
Eric Andersenc470f442003-07-28 09:56:35 +000011411 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011412 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011413 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011414 do {
11415 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011416 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011417 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011418 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011419 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011420 do {
11421 STPUTC(c, out);
11422 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011423 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011424 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011425 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011426 USTPUTC(c, out);
11427 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011428 } else {
11429 badsub:
11430 raise_error_syntax("bad substitution");
11431 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011432 if (c != '}' && subtype == VSLENGTH) {
11433 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011434 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011435 }
Eric Andersencb57d552001-06-28 07:25:16 +000011436
Eric Andersenc470f442003-07-28 09:56:35 +000011437 STPUTC('=', out);
11438 flags = 0;
11439 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011440 /* ${VAR...} but not $VAR or ${#VAR} */
11441 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011442 switch (c) {
11443 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011444 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011445#if ENABLE_ASH_BASH_COMPAT
11446 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011447//TODO: support more general format ${v:EXPR:EXPR},
11448// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011449 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011450 pungetc();
11451 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011452 }
11453#endif
11454 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011455 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011456 default: {
11457 static const char types[] ALIGN1 = "}-+?=";
11458 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011459 if (p == NULL)
11460 goto badsub;
11461 subtype = p - types + VSNORMAL;
11462 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011463 }
Eric Andersenc470f442003-07-28 09:56:35 +000011464 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011465 case '#': {
11466 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011467 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011468 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011469 if (c != cc)
11470 goto do_pungetc;
11471 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011472 break;
11473 }
11474#if ENABLE_ASH_BASH_COMPAT
11475 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011476 /* ${v/[/]pattern/repl} */
11477//TODO: encode pattern and repl separately.
11478// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011479 subtype = VSREPLACE;
11480 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011481 if (c != '/')
11482 goto do_pungetc;
11483 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011484 break;
11485#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011486 }
Eric Andersenc470f442003-07-28 09:56:35 +000011487 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011488 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011489 pungetc();
11490 }
11491 if (dblquote || arinest)
11492 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011493 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011494 if (subtype != VSNORMAL) {
11495 varnest++;
11496 if (dblquote || arinest) {
11497 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011498 }
11499 }
11500 }
Eric Andersenc470f442003-07-28 09:56:35 +000011501 goto parsesub_return;
11502}
Eric Andersencb57d552001-06-28 07:25:16 +000011503
Eric Andersencb57d552001-06-28 07:25:16 +000011504/*
11505 * Called to parse command substitutions. Newstyle is set if the command
11506 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11507 * list of commands (passed by reference), and savelen is the number of
11508 * characters on the top of the stack which must be preserved.
11509 */
Eric Andersenc470f442003-07-28 09:56:35 +000011510parsebackq: {
11511 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011512 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011513 union node *n;
11514 char *volatile str;
11515 struct jmploc jmploc;
11516 struct jmploc *volatile savehandler;
11517 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011518 smallint saveprompt = 0;
11519
Eric Andersencb57d552001-06-28 07:25:16 +000011520#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011521 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011522#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011523 savepbq = parsebackquote;
11524 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011525 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011526 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011527 exception_handler = savehandler;
11528 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011529 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011530 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011531 str = NULL;
11532 savelen = out - (char *)stackblock();
11533 if (savelen > 0) {
11534 str = ckmalloc(savelen);
11535 memcpy(str, stackblock(), savelen);
11536 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011537 savehandler = exception_handler;
11538 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011539 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011540 if (oldstyle) {
11541 /* We must read until the closing backquote, giving special
11542 treatment to some slashes, and then push the string and
11543 reread it as input, interpreting it normally. */
11544 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011545 size_t psavelen;
11546 char *pstr;
11547
Eric Andersenc470f442003-07-28 09:56:35 +000011548 STARTSTACKSTR(pout);
11549 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011550 int pc;
11551
11552 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011553 pc = pgetc();
11554 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011555 case '`':
11556 goto done;
11557
11558 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011559 pc = pgetc();
11560 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011561 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011562 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011563 /*
11564 * If eating a newline, avoid putting
11565 * the newline into the new character
11566 * stream (via the STPUTC after the
11567 * switch).
11568 */
11569 continue;
11570 }
11571 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011572 && (!dblquote || pc != '"')
11573 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011574 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011575 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011576 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011577 break;
11578 }
11579 /* fall through */
11580
11581 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011582 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011583 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011584 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011585
11586 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011587 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011588 needprompt = doprompt;
11589 break;
11590
11591 default:
11592 break;
11593 }
11594 STPUTC(pc, pout);
11595 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011596 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011597 STPUTC('\0', pout);
11598 psavelen = pout - (char *)stackblock();
11599 if (psavelen > 0) {
11600 pstr = grabstackstr(pout);
11601 setinputstring(pstr);
11602 }
11603 }
11604 nlpp = &bqlist;
11605 while (*nlpp)
11606 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011607 *nlpp = stzalloc(sizeof(**nlpp));
11608 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011609 parsebackquote = oldstyle;
11610
11611 if (oldstyle) {
11612 saveprompt = doprompt;
11613 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011614 }
11615
Eric Andersenc470f442003-07-28 09:56:35 +000011616 n = list(2);
11617
11618 if (oldstyle)
11619 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011620 else if (readtoken() != TRP)
11621 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011622
11623 (*nlpp)->n = n;
11624 if (oldstyle) {
11625 /*
11626 * Start reading from old file again, ignoring any pushed back
11627 * tokens left from the backquote parsing
11628 */
11629 popfile();
11630 tokpushback = 0;
11631 }
11632 while (stackblocksize() <= savelen)
11633 growstackblock();
11634 STARTSTACKSTR(out);
11635 if (str) {
11636 memcpy(out, str, savelen);
11637 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011638 INT_OFF;
11639 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011640 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011641 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011642 }
11643 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011644 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011645 if (arinest || dblquote)
11646 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11647 else
11648 USTPUTC(CTLBACKQ, out);
11649 if (oldstyle)
11650 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011651 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011652}
11653
Mike Frysinger98c52642009-04-02 10:02:37 +000011654#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011655/*
11656 * Parse an arithmetic expansion (indicate start of one and set state)
11657 */
Eric Andersenc470f442003-07-28 09:56:35 +000011658parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011659 if (++arinest == 1) {
11660 prevsyntax = syntax;
11661 syntax = ARISYNTAX;
11662 USTPUTC(CTLARI, out);
11663 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011664 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011665 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011666 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011667 } else {
11668 /*
11669 * we collapse embedded arithmetic expansion to
11670 * parenthesis, which should be equivalent
11671 */
11672 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011673 }
Eric Andersenc470f442003-07-28 09:56:35 +000011674 goto parsearith_return;
11675}
11676#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011677
Eric Andersenc470f442003-07-28 09:56:35 +000011678} /* end of readtoken */
11679
Eric Andersencb57d552001-06-28 07:25:16 +000011680/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011681 * Read the next input token.
11682 * If the token is a word, we set backquotelist to the list of cmds in
11683 * backquotes. We set quoteflag to true if any part of the word was
11684 * quoted.
11685 * If the token is TREDIR, then we set redirnode to a structure containing
11686 * the redirection.
11687 * In all cases, the variable startlinno is set to the number of the line
11688 * on which the token starts.
11689 *
11690 * [Change comment: here documents and internal procedures]
11691 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11692 * word parsing code into a separate routine. In this case, readtoken
11693 * doesn't need to have any internal procedures, but parseword does.
11694 * We could also make parseoperator in essence the main routine, and
11695 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011696 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011697#define NEW_xxreadtoken
11698#ifdef NEW_xxreadtoken
11699/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011700static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011701 '\n', '(', ')', /* singles */
11702 '&', '|', ';', /* doubles */
11703 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011704};
Eric Andersencb57d552001-06-28 07:25:16 +000011705
Denis Vlasenko834dee72008-10-07 09:18:30 +000011706#define xxreadtoken_singles 3
11707#define xxreadtoken_doubles 3
11708
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011709static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011710 TNL, TLP, TRP, /* only single occurrence allowed */
11711 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11712 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011713 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011714};
11715
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011716static int
11717xxreadtoken(void)
11718{
11719 int c;
11720
11721 if (tokpushback) {
11722 tokpushback = 0;
11723 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011724 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011725 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011726 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011727 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011728 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011729 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011730 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011731
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011732 if (c == '#') {
11733 while ((c = pgetc()) != '\n' && c != PEOF)
11734 continue;
11735 pungetc();
11736 } else if (c == '\\') {
11737 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011738 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011739 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011740 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011741 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011742 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011743 } else {
11744 const char *p;
11745
11746 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11747 if (c != PEOF) {
11748 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011749 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011750 needprompt = doprompt;
11751 }
11752
11753 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011754 if (p == NULL)
11755 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011756
Denis Vlasenko834dee72008-10-07 09:18:30 +000011757 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11758 int cc = pgetc();
11759 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011760 p += xxreadtoken_doubles + 1;
11761 } else {
11762 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011763#if ENABLE_ASH_BASH_COMPAT
11764 if (c == '&' && cc == '>') /* &> */
11765 break; /* return readtoken1(...) */
11766#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011767 }
11768 }
11769 }
11770 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11771 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011772 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011773 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011774
11775 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011776}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011777#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011778#define RETURN(token) return lasttoken = token
11779static int
11780xxreadtoken(void)
11781{
11782 int c;
11783
11784 if (tokpushback) {
11785 tokpushback = 0;
11786 return lasttoken;
11787 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011788 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011789 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011790 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011791 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011792 switch (c) {
11793 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011794 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011795 continue;
11796 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011797 while ((c = pgetc()) != '\n' && c != PEOF)
11798 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011799 pungetc();
11800 continue;
11801 case '\\':
11802 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011803 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011804 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011805 continue;
11806 }
11807 pungetc();
11808 goto breakloop;
11809 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011810 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011811 needprompt = doprompt;
11812 RETURN(TNL);
11813 case PEOF:
11814 RETURN(TEOF);
11815 case '&':
11816 if (pgetc() == '&')
11817 RETURN(TAND);
11818 pungetc();
11819 RETURN(TBACKGND);
11820 case '|':
11821 if (pgetc() == '|')
11822 RETURN(TOR);
11823 pungetc();
11824 RETURN(TPIPE);
11825 case ';':
11826 if (pgetc() == ';')
11827 RETURN(TENDCASE);
11828 pungetc();
11829 RETURN(TSEMI);
11830 case '(':
11831 RETURN(TLP);
11832 case ')':
11833 RETURN(TRP);
11834 default:
11835 goto breakloop;
11836 }
11837 }
11838 breakloop:
11839 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11840#undef RETURN
11841}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011842#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011843
11844static int
11845readtoken(void)
11846{
11847 int t;
11848#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011849 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011850#endif
11851
11852#if ENABLE_ASH_ALIAS
11853 top:
11854#endif
11855
11856 t = xxreadtoken();
11857
11858 /*
11859 * eat newlines
11860 */
11861 if (checkkwd & CHKNL) {
11862 while (t == TNL) {
11863 parseheredoc();
11864 t = xxreadtoken();
11865 }
11866 }
11867
11868 if (t != TWORD || quoteflag) {
11869 goto out;
11870 }
11871
11872 /*
11873 * check for keywords
11874 */
11875 if (checkkwd & CHKKWD) {
11876 const char *const *pp;
11877
11878 pp = findkwd(wordtext);
11879 if (pp) {
11880 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011881 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011882 goto out;
11883 }
11884 }
11885
11886 if (checkkwd & CHKALIAS) {
11887#if ENABLE_ASH_ALIAS
11888 struct alias *ap;
11889 ap = lookupalias(wordtext, 1);
11890 if (ap != NULL) {
11891 if (*ap->val) {
11892 pushstring(ap->val, ap);
11893 }
11894 goto top;
11895 }
11896#endif
11897 }
11898 out:
11899 checkkwd = 0;
11900#if DEBUG
11901 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011902 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011903 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011904 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011905#endif
11906 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011907}
11908
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011909static char
11910peektoken(void)
11911{
11912 int t;
11913
11914 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011915 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011916 return tokname_array[t][0];
11917}
Eric Andersencb57d552001-06-28 07:25:16 +000011918
11919/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011920 * Read and parse a command. Returns NODE_EOF on end of file.
11921 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011922 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011923static union node *
11924parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011925{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011926 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011927
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011928 tokpushback = 0;
11929 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011930 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011931 needprompt = 0;
11932 t = readtoken();
11933 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011934 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011935 if (t == TNL)
11936 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011937 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011938 return list(1);
11939}
11940
11941/*
11942 * Input any here documents.
11943 */
11944static void
11945parseheredoc(void)
11946{
11947 struct heredoc *here;
11948 union node *n;
11949
11950 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011951 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011952
11953 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011954 setprompt_if(needprompt, 2);
11955 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011956 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011957 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011958 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011959 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011960 n->narg.text = wordtext;
11961 n->narg.backquote = backquotelist;
11962 here->here->nhere.doc = n;
11963 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011964 }
Eric Andersencb57d552001-06-28 07:25:16 +000011965}
11966
11967
11968/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011969 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011970 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011971#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011972static const char *
11973expandstr(const char *ps)
11974{
11975 union node n;
11976
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011977 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11978 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011979 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011980 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011981 popfile();
11982
11983 n.narg.type = NARG;
11984 n.narg.next = NULL;
11985 n.narg.text = wordtext;
11986 n.narg.backquote = backquotelist;
11987
11988 expandarg(&n, NULL, 0);
11989 return stackblock();
11990}
11991#endif
11992
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011993/*
11994 * Execute a command or commands contained in a string.
11995 */
11996static int
11997evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011998{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011999 union node *n;
12000 struct stackmark smark;
12001 int skip;
12002
12003 setinputstring(s);
12004 setstackmark(&smark);
12005
12006 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012007 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012008 evaltree(n, 0);
12009 popstackmark(&smark);
12010 skip = evalskip;
12011 if (skip)
12012 break;
12013 }
12014 popfile();
12015
12016 skip &= mask;
12017 evalskip = skip;
12018 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012019}
12020
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012021/*
12022 * The eval command.
12023 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012024static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012025evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012026{
12027 char *p;
12028 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012029
Denis Vlasenko68404f12008-03-17 09:00:54 +000012030 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012031 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012032 argv += 2;
12033 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012034 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012035 for (;;) {
12036 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012037 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012038 if (p == NULL)
12039 break;
12040 STPUTC(' ', concat);
12041 }
12042 STPUTC('\0', concat);
12043 p = grabstackstr(concat);
12044 }
12045 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012046 }
12047 return exitstatus;
12048}
12049
12050/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012051 * Read and execute commands.
12052 * "Top" is nonzero for the top level command loop;
12053 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012054 */
12055static int
12056cmdloop(int top)
12057{
12058 union node *n;
12059 struct stackmark smark;
12060 int inter;
12061 int numeof = 0;
12062
12063 TRACE(("cmdloop(%d) called\n", top));
12064 for (;;) {
12065 int skip;
12066
12067 setstackmark(&smark);
12068#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012069 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012070 showjobs(stderr, SHOW_CHANGED);
12071#endif
12072 inter = 0;
12073 if (iflag && top) {
12074 inter++;
12075#if ENABLE_ASH_MAIL
12076 chkmail();
12077#endif
12078 }
12079 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012080#if DEBUG
12081 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012082 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012083#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012084 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012085 if (!top || numeof >= 50)
12086 break;
12087 if (!stoppedjobs()) {
12088 if (!Iflag)
12089 break;
12090 out2str("\nUse \"exit\" to leave shell.\n");
12091 }
12092 numeof++;
12093 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012094 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12095 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012096 numeof = 0;
12097 evaltree(n, 0);
12098 }
12099 popstackmark(&smark);
12100 skip = evalskip;
12101
12102 if (skip) {
12103 evalskip = 0;
12104 return skip & SKIPEVAL;
12105 }
12106 }
12107 return 0;
12108}
12109
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012110/*
12111 * Take commands from a file. To be compatible we should do a path
12112 * search for the file, which is necessary to find sub-commands.
12113 */
12114static char *
12115find_dot_file(char *name)
12116{
12117 char *fullname;
12118 const char *path = pathval();
12119 struct stat statb;
12120
12121 /* don't try this for absolute or relative paths */
12122 if (strchr(name, '/'))
12123 return name;
12124
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012125 /* IIRC standards do not say whether . is to be searched.
12126 * And it is even smaller this way, making it unconditional for now:
12127 */
12128 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12129 fullname = name;
12130 goto try_cur_dir;
12131 }
12132
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012133 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012134 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012135 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12136 /*
12137 * Don't bother freeing here, since it will
12138 * be freed by the caller.
12139 */
12140 return fullname;
12141 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012142 if (fullname != name)
12143 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012144 }
12145
12146 /* not found in the PATH */
12147 ash_msg_and_raise_error("%s: not found", name);
12148 /* NOTREACHED */
12149}
12150
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012151static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012152dotcmd(int argc, char **argv)
12153{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012154 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012155 struct strlist *sp;
12156 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012157
12158 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012159 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012160
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012161 if (!argv[1]) {
12162 /* bash says: "bash: .: filename argument required" */
12163 return 2; /* bash compat */
12164 }
12165
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012166 /* "false; . empty_file; echo $?" should print 0, not 1: */
12167 exitstatus = 0;
12168
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012169 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012170
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012171 argv += 2;
12172 argc -= 2;
12173 if (argc) { /* argc > 0, argv[0] != NULL */
12174 saveparam = shellparam;
12175 shellparam.malloced = 0;
12176 shellparam.nparam = argc;
12177 shellparam.p = argv;
12178 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012179
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012180 setinputfile(fullname, INPUT_PUSH_FILE);
12181 commandname = fullname;
12182 cmdloop(0);
12183 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012184
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012185 if (argc) {
12186 freeparam(&shellparam);
12187 shellparam = saveparam;
12188 };
12189
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012190 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012191}
12192
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012193static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012194exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012195{
12196 if (stoppedjobs())
12197 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012198 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012199 exitstatus = number(argv[1]);
12200 raise_exception(EXEXIT);
12201 /* NOTREACHED */
12202}
12203
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012204/*
12205 * Read a file containing shell functions.
12206 */
12207static void
12208readcmdfile(char *name)
12209{
12210 setinputfile(name, INPUT_PUSH_FILE);
12211 cmdloop(0);
12212 popfile();
12213}
12214
12215
Denis Vlasenkocc571512007-02-23 21:10:35 +000012216/* ============ find_command inplementation */
12217
12218/*
12219 * Resolve a command name. If you change this routine, you may have to
12220 * change the shellexec routine as well.
12221 */
12222static void
12223find_command(char *name, struct cmdentry *entry, int act, const char *path)
12224{
12225 struct tblentry *cmdp;
12226 int idx;
12227 int prev;
12228 char *fullname;
12229 struct stat statb;
12230 int e;
12231 int updatetbl;
12232 struct builtincmd *bcmd;
12233
12234 /* If name contains a slash, don't use PATH or hash table */
12235 if (strchr(name, '/') != NULL) {
12236 entry->u.index = -1;
12237 if (act & DO_ABS) {
12238 while (stat(name, &statb) < 0) {
12239#ifdef SYSV
12240 if (errno == EINTR)
12241 continue;
12242#endif
12243 entry->cmdtype = CMDUNKNOWN;
12244 return;
12245 }
12246 }
12247 entry->cmdtype = CMDNORMAL;
12248 return;
12249 }
12250
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012251/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012252
12253 updatetbl = (path == pathval());
12254 if (!updatetbl) {
12255 act |= DO_ALTPATH;
12256 if (strstr(path, "%builtin") != NULL)
12257 act |= DO_ALTBLTIN;
12258 }
12259
12260 /* If name is in the table, check answer will be ok */
12261 cmdp = cmdlookup(name, 0);
12262 if (cmdp != NULL) {
12263 int bit;
12264
12265 switch (cmdp->cmdtype) {
12266 default:
12267#if DEBUG
12268 abort();
12269#endif
12270 case CMDNORMAL:
12271 bit = DO_ALTPATH;
12272 break;
12273 case CMDFUNCTION:
12274 bit = DO_NOFUNC;
12275 break;
12276 case CMDBUILTIN:
12277 bit = DO_ALTBLTIN;
12278 break;
12279 }
12280 if (act & bit) {
12281 updatetbl = 0;
12282 cmdp = NULL;
12283 } else if (cmdp->rehash == 0)
12284 /* if not invalidated by cd, we're done */
12285 goto success;
12286 }
12287
12288 /* If %builtin not in path, check for builtin next */
12289 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012290 if (bcmd) {
12291 if (IS_BUILTIN_REGULAR(bcmd))
12292 goto builtin_success;
12293 if (act & DO_ALTPATH) {
12294 if (!(act & DO_ALTBLTIN))
12295 goto builtin_success;
12296 } else if (builtinloc <= 0) {
12297 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012298 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012299 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012300
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012301#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012302 {
12303 int applet_no = find_applet_by_name(name);
12304 if (applet_no >= 0) {
12305 entry->cmdtype = CMDNORMAL;
12306 entry->u.index = -2 - applet_no;
12307 return;
12308 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012309 }
12310#endif
12311
Denis Vlasenkocc571512007-02-23 21:10:35 +000012312 /* We have to search path. */
12313 prev = -1; /* where to start */
12314 if (cmdp && cmdp->rehash) { /* doing a rehash */
12315 if (cmdp->cmdtype == CMDBUILTIN)
12316 prev = builtinloc;
12317 else
12318 prev = cmdp->param.index;
12319 }
12320
12321 e = ENOENT;
12322 idx = -1;
12323 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012324 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012325 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012326 /* NB: code below will still use fullname
12327 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012328 idx++;
12329 if (pathopt) {
12330 if (prefix(pathopt, "builtin")) {
12331 if (bcmd)
12332 goto builtin_success;
12333 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012334 }
12335 if ((act & DO_NOFUNC)
12336 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012337 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012338 continue;
12339 }
12340 }
12341 /* if rehash, don't redo absolute path names */
12342 if (fullname[0] == '/' && idx <= prev) {
12343 if (idx < prev)
12344 continue;
12345 TRACE(("searchexec \"%s\": no change\n", name));
12346 goto success;
12347 }
12348 while (stat(fullname, &statb) < 0) {
12349#ifdef SYSV
12350 if (errno == EINTR)
12351 continue;
12352#endif
12353 if (errno != ENOENT && errno != ENOTDIR)
12354 e = errno;
12355 goto loop;
12356 }
12357 e = EACCES; /* if we fail, this will be the error */
12358 if (!S_ISREG(statb.st_mode))
12359 continue;
12360 if (pathopt) { /* this is a %func directory */
12361 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012362 /* NB: stalloc will return space pointed by fullname
12363 * (because we don't have any intervening allocations
12364 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012365 readcmdfile(fullname);
12366 cmdp = cmdlookup(name, 0);
12367 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12368 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12369 stunalloc(fullname);
12370 goto success;
12371 }
12372 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12373 if (!updatetbl) {
12374 entry->cmdtype = CMDNORMAL;
12375 entry->u.index = idx;
12376 return;
12377 }
12378 INT_OFF;
12379 cmdp = cmdlookup(name, 1);
12380 cmdp->cmdtype = CMDNORMAL;
12381 cmdp->param.index = idx;
12382 INT_ON;
12383 goto success;
12384 }
12385
12386 /* We failed. If there was an entry for this command, delete it */
12387 if (cmdp && updatetbl)
12388 delete_cmd_entry();
12389 if (act & DO_ERR)
12390 ash_msg("%s: %s", name, errmsg(e, "not found"));
12391 entry->cmdtype = CMDUNKNOWN;
12392 return;
12393
12394 builtin_success:
12395 if (!updatetbl) {
12396 entry->cmdtype = CMDBUILTIN;
12397 entry->u.cmd = bcmd;
12398 return;
12399 }
12400 INT_OFF;
12401 cmdp = cmdlookup(name, 1);
12402 cmdp->cmdtype = CMDBUILTIN;
12403 cmdp->param.cmd = bcmd;
12404 INT_ON;
12405 success:
12406 cmdp->rehash = 0;
12407 entry->cmdtype = cmdp->cmdtype;
12408 entry->u = cmdp->param;
12409}
12410
12411
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012412/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012413
Eric Andersencb57d552001-06-28 07:25:16 +000012414/*
Eric Andersencb57d552001-06-28 07:25:16 +000012415 * The trap builtin.
12416 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012417static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012418trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012419{
12420 char *action;
12421 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012422 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012423
Eric Andersenc470f442003-07-28 09:56:35 +000012424 nextopt(nullstr);
12425 ap = argptr;
12426 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012427 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012428 char *tr = trap_ptr[signo];
12429 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012430 /* note: bash adds "SIG", but only if invoked
12431 * as "bash". If called as "sh", or if set -o posix,
12432 * then it prints short signal names.
12433 * We are printing short names: */
12434 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012435 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012436 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012437 /* trap_ptr != trap only if we are in special-cased `trap` code.
12438 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012439 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012440 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012441 }
12442 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012443 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012444 if (trap_ptr != trap) {
12445 free(trap_ptr);
12446 trap_ptr = trap;
12447 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012448 */
Eric Andersencb57d552001-06-28 07:25:16 +000012449 return 0;
12450 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012451
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012452 action = NULL;
12453 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012454 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012455 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012456 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012457 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012458 if (signo < 0) {
12459 /* Mimic bash message exactly */
12460 ash_msg("%s: invalid signal specification", *ap);
12461 exitcode = 1;
12462 goto next;
12463 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012464 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012465 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012466 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012467 action = NULL;
12468 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012469 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012470 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012471 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012472 if (action)
12473 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012474 trap[signo] = action;
12475 if (signo != 0)
12476 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012477 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012478 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012479 ap++;
12480 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012481 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012482}
12483
Eric Andersenc470f442003-07-28 09:56:35 +000012484
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012485/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012486
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012487#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012488/*
12489 * Lists available builtins
12490 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012491static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012492helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012493{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012494 unsigned col;
12495 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012496
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012497 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012498 "Built-in commands:\n"
12499 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012500 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012501 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012502 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012503 if (col > 60) {
12504 out1fmt("\n");
12505 col = 0;
12506 }
12507 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012508#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012509 {
12510 const char *a = applet_names;
12511 while (*a) {
12512 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12513 if (col > 60) {
12514 out1fmt("\n");
12515 col = 0;
12516 }
12517 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012518 }
12519 }
12520#endif
12521 out1fmt("\n\n");
12522 return EXIT_SUCCESS;
12523}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012524#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012525
Eric Andersencb57d552001-06-28 07:25:16 +000012526/*
Eric Andersencb57d552001-06-28 07:25:16 +000012527 * The export and readonly commands.
12528 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012529static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012530exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012531{
12532 struct var *vp;
12533 char *name;
12534 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012535 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012536 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012537
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012538 if (nextopt("p") != 'p') {
12539 aptr = argptr;
12540 name = *aptr;
12541 if (name) {
12542 do {
12543 p = strchr(name, '=');
12544 if (p != NULL) {
12545 p++;
12546 } else {
12547 vp = *findvar(hashvar(name), name);
12548 if (vp) {
12549 vp->flags |= flag;
12550 continue;
12551 }
Eric Andersencb57d552001-06-28 07:25:16 +000012552 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012553 setvar(name, p, flag);
12554 } while ((name = *++aptr) != NULL);
12555 return 0;
12556 }
Eric Andersencb57d552001-06-28 07:25:16 +000012557 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012558 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012559 return 0;
12560}
12561
Eric Andersencb57d552001-06-28 07:25:16 +000012562/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012563 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012564 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012565static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012566unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012567{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012568 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012569
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012570 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012571 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012572 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012573}
12574
Eric Andersencb57d552001-06-28 07:25:16 +000012575/*
Eric Andersencb57d552001-06-28 07:25:16 +000012576 * The unset builtin command. We unset the function before we unset the
12577 * variable to allow a function to be unset when there is a readonly variable
12578 * with the same name.
12579 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012580static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012581unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012582{
12583 char **ap;
12584 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012585 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012586 int ret = 0;
12587
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012588 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012589 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012590 }
Eric Andersencb57d552001-06-28 07:25:16 +000012591
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012592 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012593 if (flag != 'f') {
12594 i = unsetvar(*ap);
12595 ret |= i;
12596 if (!(i & 2))
12597 continue;
12598 }
12599 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012600 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012601 }
Eric Andersenc470f442003-07-28 09:56:35 +000012602 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012603}
12604
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012605static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012606 ' ', offsetof(struct tms, tms_utime),
12607 '\n', offsetof(struct tms, tms_stime),
12608 ' ', offsetof(struct tms, tms_cutime),
12609 '\n', offsetof(struct tms, tms_cstime),
12610 0
12611};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012612static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012613timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012614{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012615 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012616 const unsigned char *p;
12617 struct tms buf;
12618
12619 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012620 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012621
12622 p = timescmd_str;
12623 do {
12624 t = *(clock_t *)(((char *) &buf) + p[1]);
12625 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012626 t = t % clk_tck;
12627 out1fmt("%lum%lu.%03lus%c",
12628 s / 60, s % 60,
12629 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012630 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012631 p += 2;
12632 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012633
Eric Andersencb57d552001-06-28 07:25:16 +000012634 return 0;
12635}
12636
Mike Frysinger98c52642009-04-02 10:02:37 +000012637#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012638/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012639 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012640 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012641 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012642 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012643 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012644static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012645letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012646{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012647 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012648
Denis Vlasenko68404f12008-03-17 09:00:54 +000012649 argv++;
12650 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012651 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012652 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012653 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012654 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012655
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012656 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012657}
Eric Andersenc470f442003-07-28 09:56:35 +000012658#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012659
Eric Andersenc470f442003-07-28 09:56:35 +000012660/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012661 * The read builtin. Options:
12662 * -r Do not interpret '\' specially
12663 * -s Turn off echo (tty only)
12664 * -n NCHARS Read NCHARS max
12665 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12666 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12667 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012668 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012669 * TODO: bash also has:
12670 * -a ARRAY Read into array[0],[1],etc
12671 * -d DELIM End on DELIM char, not newline
12672 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012673 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012674static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012675readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012676{
Denys Vlasenko73067272010-01-12 22:11:24 +010012677 char *opt_n = NULL;
12678 char *opt_p = NULL;
12679 char *opt_t = NULL;
12680 char *opt_u = NULL;
12681 int read_flags = 0;
12682 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012683 int i;
12684
Denys Vlasenko73067272010-01-12 22:11:24 +010012685 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012686 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012687 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012688 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012689 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012690 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012691 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012692 break;
12693 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012694 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012695 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012696 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012697 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012698 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012699 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012700 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012701 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012702 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012703 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012704 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012705 default:
12706 break;
12707 }
Eric Andersenc470f442003-07-28 09:56:35 +000012708 }
Paul Fox02eb9342005-09-07 16:56:02 +000012709
Denys Vlasenko03dad222010-01-12 23:29:57 +010012710 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012711 argptr,
12712 bltinlookup("IFS"), /* can be NULL */
12713 read_flags,
12714 opt_n,
12715 opt_p,
12716 opt_t,
12717 opt_u
12718 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012719
Denys Vlasenko73067272010-01-12 22:11:24 +010012720 if ((uintptr_t)r > 1)
12721 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012722
Denys Vlasenko73067272010-01-12 22:11:24 +010012723 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012724}
12725
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012726static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012727umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012728{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012729 static const char permuser[3] ALIGN1 = "ugo";
12730 static const char permmode[3] ALIGN1 = "rwx";
12731 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012732 S_IRUSR, S_IWUSR, S_IXUSR,
12733 S_IRGRP, S_IWGRP, S_IXGRP,
12734 S_IROTH, S_IWOTH, S_IXOTH
12735 };
12736
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012737 /* TODO: use bb_parse_mode() instead */
12738
Eric Andersenc470f442003-07-28 09:56:35 +000012739 char *ap;
12740 mode_t mask;
12741 int i;
12742 int symbolic_mode = 0;
12743
12744 while (nextopt("S") != '\0') {
12745 symbolic_mode = 1;
12746 }
12747
Denis Vlasenkob012b102007-02-19 22:43:01 +000012748 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012749 mask = umask(0);
12750 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012751 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012752
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012753 ap = *argptr;
12754 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012755 if (symbolic_mode) {
12756 char buf[18];
12757 char *p = buf;
12758
12759 for (i = 0; i < 3; i++) {
12760 int j;
12761
12762 *p++ = permuser[i];
12763 *p++ = '=';
12764 for (j = 0; j < 3; j++) {
12765 if ((mask & permmask[3 * i + j]) == 0) {
12766 *p++ = permmode[j];
12767 }
12768 }
12769 *p++ = ',';
12770 }
12771 *--p = 0;
12772 puts(buf);
12773 } else {
12774 out1fmt("%.4o\n", mask);
12775 }
12776 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012777 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012778 mask = 0;
12779 do {
12780 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012781 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012782 mask = (mask << 3) + (*ap - '0');
12783 } while (*++ap != '\0');
12784 umask(mask);
12785 } else {
12786 mask = ~mask & 0777;
12787 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012788 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012789 }
12790 umask(~mask & 0777);
12791 }
12792 }
12793 return 0;
12794}
12795
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012796static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012797ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012798{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012799 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012800}
12801
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012802/* ============ main() and helpers */
12803
12804/*
12805 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012806 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012807static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012808static void
12809exitshell(void)
12810{
12811 struct jmploc loc;
12812 char *p;
12813 int status;
12814
12815 status = exitstatus;
12816 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12817 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012818 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012819/* dash bug: it just does _exit(exitstatus) here
12820 * but we have to do setjobctl(0) first!
12821 * (bug is still not fixed in dash-0.5.3 - if you run dash
12822 * under Midnight Commander, on exit from dash MC is backgrounded) */
12823 status = exitstatus;
12824 goto out;
12825 }
12826 exception_handler = &loc;
12827 p = trap[0];
12828 if (p) {
12829 trap[0] = NULL;
12830 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012831 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012832 }
12833 flush_stdout_stderr();
12834 out:
12835 setjobctl(0);
12836 _exit(status);
12837 /* NOTREACHED */
12838}
12839
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012840static void
12841init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012842{
12843 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012844 /* we will never free this */
12845 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012846
12847 /* from trap.c: */
12848 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012849 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12850 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12851 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012852 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012853
12854 /* from var.c: */
12855 {
12856 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012857 const char *p;
12858 struct stat st1, st2;
12859
12860 initvar();
12861 for (envp = environ; envp && *envp; envp++) {
12862 if (strchr(*envp, '=')) {
12863 setvareq(*envp, VEXPORT|VTEXTFIXED);
12864 }
12865 }
12866
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012867 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012868
12869 p = lookupvar("PWD");
12870 if (p)
12871 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12872 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12873 p = '\0';
12874 setpwd(p, 0);
12875 }
12876}
12877
12878/*
12879 * Process the shell command line arguments.
12880 */
12881static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012882procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012883{
12884 int i;
12885 const char *xminusc;
12886 char **xargv;
12887
12888 xargv = argv;
12889 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012890 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012891 xargv++;
12892 for (i = 0; i < NOPTS; i++)
12893 optlist[i] = 2;
12894 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012895 if (options(1)) {
12896 /* it already printed err message */
12897 raise_exception(EXERROR);
12898 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012899 xargv = argptr;
12900 xminusc = minusc;
12901 if (*xargv == NULL) {
12902 if (xminusc)
12903 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12904 sflag = 1;
12905 }
12906 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12907 iflag = 1;
12908 if (mflag == 2)
12909 mflag = iflag;
12910 for (i = 0; i < NOPTS; i++)
12911 if (optlist[i] == 2)
12912 optlist[i] = 0;
12913#if DEBUG == 2
12914 debug = 1;
12915#endif
12916 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12917 if (xminusc) {
12918 minusc = *xargv++;
12919 if (*xargv)
12920 goto setarg0;
12921 } else if (!sflag) {
12922 setinputfile(*xargv, 0);
12923 setarg0:
12924 arg0 = *xargv++;
12925 commandname = arg0;
12926 }
12927
12928 shellparam.p = xargv;
12929#if ENABLE_ASH_GETOPTS
12930 shellparam.optind = 1;
12931 shellparam.optoff = -1;
12932#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012933 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012934 while (*xargv) {
12935 shellparam.nparam++;
12936 xargv++;
12937 }
12938 optschanged();
12939}
12940
12941/*
12942 * Read /etc/profile or .profile.
12943 */
12944static void
12945read_profile(const char *name)
12946{
12947 int skip;
12948
12949 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12950 return;
12951 skip = cmdloop(0);
12952 popfile();
12953 if (skip)
12954 exitshell();
12955}
12956
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012957/*
12958 * This routine is called when an error or an interrupt occurs in an
12959 * interactive shell and control is returned to the main command loop.
12960 */
12961static void
12962reset(void)
12963{
12964 /* from eval.c: */
12965 evalskip = 0;
12966 loopnest = 0;
12967 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000012968 g_parsefile->left_in_buffer = 0;
12969 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012970 popallfiles();
12971 /* from parser.c: */
12972 tokpushback = 0;
12973 checkkwd = 0;
12974 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000012975 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012976}
12977
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012978#if PROFILE
12979static short profile_buf[16384];
12980extern int etext();
12981#endif
12982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012983/*
12984 * Main routine. We initialize things, parse the arguments, execute
12985 * profiles if we're a login shell, and then call cmdloop to execute
12986 * commands. The setjmp call sets up the location to jump to when an
12987 * exception occurs. When an exception occurs the variable "state"
12988 * is used to figure out how far we had gotten.
12989 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012990int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012991int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012992{
Mike Frysinger98c52642009-04-02 10:02:37 +000012993 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000012994 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012995 struct jmploc jmploc;
12996 struct stackmark smark;
12997
Denis Vlasenko01631112007-12-16 17:20:38 +000012998 /* Initialize global data */
12999 INIT_G_misc();
13000 INIT_G_memstack();
13001 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013002#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013003 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013004#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013005 INIT_G_cmdtable();
13006
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013007#if PROFILE
13008 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13009#endif
13010
13011#if ENABLE_FEATURE_EDITING
13012 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13013#endif
13014 state = 0;
13015 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013016 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013017 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013018
13019 reset();
13020
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013021 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013022 if (e == EXERROR)
13023 exitstatus = 2;
13024 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013025 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013026 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013027 }
13028 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013029 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013030 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013031
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013032 popstackmark(&smark);
13033 FORCE_INT_ON; /* enable interrupts */
13034 if (s == 1)
13035 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013036 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013037 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013038 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013039 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013040 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013041 }
13042 exception_handler = &jmploc;
13043#if DEBUG
13044 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013045 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013046 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013047#endif
13048 rootpid = getpid();
13049
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013050 init();
13051 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013052 procargs(argv);
13053
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013054#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13055 if (iflag) {
13056 const char *hp = lookupvar("HISTFILE");
13057
13058 if (hp == NULL) {
13059 hp = lookupvar("HOME");
13060 if (hp != NULL) {
13061 char *defhp = concat_path_file(hp, ".ash_history");
13062 setvar("HISTFILE", defhp, 0);
13063 free(defhp);
13064 }
13065 }
13066 }
13067#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013068 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013069 isloginsh = 1;
13070 if (isloginsh) {
13071 state = 1;
13072 read_profile("/etc/profile");
13073 state1:
13074 state = 2;
13075 read_profile(".profile");
13076 }
13077 state2:
13078 state = 3;
13079 if (
13080#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013081 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013082#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013083 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013084 ) {
13085 shinit = lookupvar("ENV");
13086 if (shinit != NULL && *shinit != '\0') {
13087 read_profile(shinit);
13088 }
13089 }
13090 state3:
13091 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013092 if (minusc) {
13093 /* evalstring pushes parsefile stack.
13094 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013095 * is one of stacked source fds.
13096 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013097 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013098 // ^^ not necessary since now we special-case fd 0
13099 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013100 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013101 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013102
13103 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013104#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013105 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013106 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013107 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013108 line_input_state->hist_file = hp;
13109 }
13110#endif
13111 state4: /* XXX ??? - why isn't this before the "if" statement */
13112 cmdloop(1);
13113 }
13114#if PROFILE
13115 monitor(0);
13116#endif
13117#ifdef GPROF
13118 {
13119 extern void _mcleanup(void);
13120 _mcleanup();
13121 }
13122#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013123 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013124 exitshell();
13125 /* NOTREACHED */
13126}
13127
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013128
Eric Andersendf82f612001-06-28 07:46:40 +000013129/*-
13130 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013131 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013132 *
13133 * This code is derived from software contributed to Berkeley by
13134 * Kenneth Almquist.
13135 *
13136 * Redistribution and use in source and binary forms, with or without
13137 * modification, are permitted provided that the following conditions
13138 * are met:
13139 * 1. Redistributions of source code must retain the above copyright
13140 * notice, this list of conditions and the following disclaimer.
13141 * 2. Redistributions in binary form must reproduce the above copyright
13142 * notice, this list of conditions and the following disclaimer in the
13143 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013144 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013145 * may be used to endorse or promote products derived from this software
13146 * without specific prior written permission.
13147 *
13148 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13149 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13150 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13151 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13152 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13153 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13154 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13155 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13156 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13157 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13158 * SUCH DAMAGE.
13159 */