blob: 9b33e78eccca643fbe99a2a8de2499789ee7bd86 [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 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000016 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
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"
Mike Frysinger98c52642009-04-02 10:02:37 +000046#include "math.h"
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020047#if ENABLE_ASH_RANDOM_SUPPORT
48# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020049#else
50# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020051#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000052
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020053#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010054#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000055/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020056# undef CONFIG_FEATURE_SH_STANDALONE
57# undef ENABLE_FEATURE_SH_STANDALONE
58# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010059# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020060# define ENABLE_FEATURE_SH_STANDALONE 0
61# define IF_FEATURE_SH_STANDALONE(...)
62# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000063#endif
64
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000065#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000066# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000067#endif
68
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010069#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000070# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000071#endif
72
Denys Vlasenko771f1992010-07-16 14:31:34 +020073//applet:IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP))
74//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh))
75//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
76
77//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
78//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
79
80//config:config ASH
81//config: bool "ash"
82//config: default y
83//config: depends on !NOMMU
84//config: help
85//config: Tha 'ash' shell adds about 60k in the default configuration and is
86//config: the most complete and most pedantically correct shell included with
87//config: busybox. This shell is actually a derivative of the Debian 'dash'
88//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
89//config: (written by Kenneth Almquist) from NetBSD.
90//config:
91//config:config ASH_BASH_COMPAT
92//config: bool "bash-compatible extensions"
93//config: default y
94//config: depends on ASH
95//config: help
96//config: Enable bash-compatible extensions.
97//config:
98//config:config ASH_JOB_CONTROL
99//config: bool "Job control"
100//config: default y
101//config: depends on ASH
102//config: help
103//config: Enable job control in the ash shell.
104//config:
105//config:config ASH_ALIAS
106//config: bool "alias support"
107//config: default y
108//config: depends on ASH
109//config: help
110//config: Enable alias support in the ash shell.
111//config:
112//config:config ASH_GETOPTS
113//config: bool "Builtin getopt to parse positional parameters"
114//config: default y
115//config: depends on ASH
116//config: help
117//config: Enable getopts builtin in the ash shell.
118//config:
119//config:config ASH_BUILTIN_ECHO
120//config: bool "Builtin version of 'echo'"
121//config: default y
122//config: depends on ASH
123//config: help
124//config: Enable support for echo, builtin to ash.
125//config:
126//config:config ASH_BUILTIN_PRINTF
127//config: bool "Builtin version of 'printf'"
128//config: default y
129//config: depends on ASH
130//config: help
131//config: Enable support for printf, builtin to ash.
132//config:
133//config:config ASH_BUILTIN_TEST
134//config: bool "Builtin version of 'test'"
135//config: default y
136//config: depends on ASH
137//config: help
138//config: Enable support for test, builtin to ash.
139//config:
140//config:config ASH_CMDCMD
141//config: bool "'command' command to override shell builtins"
142//config: default y
143//config: depends on ASH
144//config: help
145//config: Enable support for the ash 'command' builtin, which allows
146//config: you to run the specified command with the specified arguments,
147//config: even when there is an ash builtin command with the same name.
148//config:
149//config:config ASH_MAIL
150//config: bool "Check for new mail on interactive shells"
151//config: default n
152//config: depends on ASH
153//config: help
154//config: Enable "check for new mail" in the ash shell.
155//config:
156//config:config ASH_OPTIMIZE_FOR_SIZE
157//config: bool "Optimize for size instead of speed"
158//config: default y
159//config: depends on ASH
160//config: help
161//config: Compile ash for reduced size at the price of speed.
162//config:
163//config:config ASH_RANDOM_SUPPORT
164//config: bool "Pseudorandom generator and $RANDOM variable"
165//config: default y
166//config: depends on ASH
167//config: help
168//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
169//config: Each read of "$RANDOM" will generate a new pseudorandom value.
170//config: You can reset the generator by using a specified start value.
171//config: After "unset RANDOM" the generator will switch off and this
172//config: variable will no longer have special treatment.
173//config:
174//config:config ASH_EXPAND_PRMT
175//config: bool "Expand prompt string"
176//config: default y
177//config: depends on ASH
178//config: help
179//config: "PS#" may contain volatile content, such as backquote commands.
180//config: This option recreates the prompt string from the environment
181//config: variable each time it is displayed.
182
183//usage:#define ash_trivial_usage NOUSAGE_STR
184//usage:#define ash_full_usage ""
185//usage:#define sh_trivial_usage NOUSAGE_STR
186//usage:#define sh_full_usage ""
187//usage:#define bash_trivial_usage NOUSAGE_STR
188//usage:#define bash_full_usage ""
189
Denis Vlasenkob012b102007-02-19 22:43:01 +0000190
Denis Vlasenko01631112007-12-16 17:20:38 +0000191/* ============ Hash table sizes. Configurable. */
192
193#define VTABSIZE 39
194#define ATABSIZE 39
195#define CMDTABLESIZE 31 /* should be prime */
196
197
Denis Vlasenkob012b102007-02-19 22:43:01 +0000198/* ============ Shell options */
199
200static const char *const optletters_optnames[] = {
201 "e" "errexit",
202 "f" "noglob",
203 "I" "ignoreeof",
204 "i" "interactive",
205 "m" "monitor",
206 "n" "noexec",
207 "s" "stdin",
208 "x" "xtrace",
209 "v" "verbose",
210 "C" "noclobber",
211 "a" "allexport",
212 "b" "notify",
213 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100214 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100215#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100216 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100217#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000218#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000219 ,"\0" "nolog"
220 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000221#endif
222};
223
Denys Vlasenko285ad152009-12-04 23:02:27 +0100224#define optletters(n) optletters_optnames[n][0]
225#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000226
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000227enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000228
Eric Andersenc470f442003-07-28 09:56:35 +0000229
Denis Vlasenkob012b102007-02-19 22:43:01 +0000230/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000231
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200232#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000233
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000234/*
Eric Andersenc470f442003-07-28 09:56:35 +0000235 * We enclose jmp_buf in a structure so that we can declare pointers to
236 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000237 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000238 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000239 * exception handlers, the user should save the value of handler on entry
240 * to an inner scope, set handler to point to a jmploc structure for the
241 * inner scope, and restore handler on exit from the scope.
242 */
Eric Andersenc470f442003-07-28 09:56:35 +0000243struct jmploc {
244 jmp_buf loc;
245};
Denis Vlasenko01631112007-12-16 17:20:38 +0000246
247struct globals_misc {
248 /* pid of main shell */
249 int rootpid;
250 /* shell level: 0 for the main shell, 1 for its children, and so on */
251 int shlvl;
252#define rootshell (!shlvl)
253 char *minusc; /* argument to -c option */
254
255 char *curdir; // = nullstr; /* current working directory */
256 char *physdir; // = nullstr; /* physical working directory */
257
258 char *arg0; /* value of $0 */
259
260 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000261
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200262 volatile int suppress_int; /* counter */
263 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000264 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200265 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000266 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000267 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000268#define EXINT 0 /* SIGINT received */
269#define EXERROR 1 /* a generic error */
270#define EXSHELLPROC 2 /* execute a shell procedure */
271#define EXEXEC 3 /* command execution failed */
272#define EXEXIT 4 /* exit the shell */
273#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000274
Denis Vlasenko01631112007-12-16 17:20:38 +0000275 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000276 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000277
278 char optlist[NOPTS];
279#define eflag optlist[0]
280#define fflag optlist[1]
281#define Iflag optlist[2]
282#define iflag optlist[3]
283#define mflag optlist[4]
284#define nflag optlist[5]
285#define sflag optlist[6]
286#define xflag optlist[7]
287#define vflag optlist[8]
288#define Cflag optlist[9]
289#define aflag optlist[10]
290#define bflag optlist[11]
291#define uflag optlist[12]
292#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100293#if ENABLE_ASH_BASH_COMPAT
294# define pipefail optlist[14]
295#else
296# define pipefail 0
297#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000298#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100299# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
300# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000301#endif
302
303 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000304 /*
305 * Sigmode records the current value of the signal handlers for the various
306 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000307 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000308 */
309 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000310#define S_DFL 1 /* default signal handling (SIG_DFL) */
311#define S_CATCH 2 /* signal is caught */
312#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000313#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000314
Denis Vlasenko01631112007-12-16 17:20:38 +0000315 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000316 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200317 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000318 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200319 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000320
321 /* Rarely referenced stuff */
322#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200323 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000324#endif
325 pid_t backgndpid; /* pid of last background process */
326 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000327};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000328extern struct globals_misc *const ash_ptr_to_globals_misc;
329#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000330#define rootpid (G_misc.rootpid )
331#define shlvl (G_misc.shlvl )
332#define minusc (G_misc.minusc )
333#define curdir (G_misc.curdir )
334#define physdir (G_misc.physdir )
335#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000336#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000337#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200338#define suppress_int (G_misc.suppress_int )
339#define pending_int (G_misc.pending_int )
340#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000341#define isloginsh (G_misc.isloginsh )
342#define nullstr (G_misc.nullstr )
343#define optlist (G_misc.optlist )
344#define sigmode (G_misc.sigmode )
345#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200346#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000347#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200348#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200349#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000350#define backgndpid (G_misc.backgndpid )
351#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000352#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000353 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
354 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000355 curdir = nullstr; \
356 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200357 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000358} while (0)
359
360
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000361/* ============ DEBUG */
362#if DEBUG
363static void trace_printf(const char *fmt, ...);
364static void trace_vprintf(const char *fmt, va_list va);
365# define TRACE(param) trace_printf param
366# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000367# define close(fd) do { \
368 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000369 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200370 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000371 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000372} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000373#else
374# define TRACE(param)
375# define TRACEV(param)
376#endif
377
378
Denis Vlasenko559691a2008-10-05 18:39:31 +0000379/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000380#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
381
Denis Vlasenko559691a2008-10-05 18:39:31 +0000382static int isdigit_str9(const char *str)
383{
384 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
385 while (--maxlen && isdigit(*str))
386 str++;
387 return (*str == '\0');
388}
Denis Vlasenko01631112007-12-16 17:20:38 +0000389
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200390static const char *var_end(const char *var)
391{
392 while (*var)
393 if (*var++ == '=')
394 break;
395 return var;
396}
397
Denis Vlasenko559691a2008-10-05 18:39:31 +0000398
399/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000400/*
Eric Andersen2870d962001-07-02 17:27:21 +0000401 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000402 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000403 * much more efficient and portable. (But hacking the kernel is so much
404 * more fun than worrying about efficiency and portability. :-))
405 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000406#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200407 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000408 xbarrier(); \
409} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000410
411/*
412 * Called to raise an exception. Since C doesn't include exceptions, we
413 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000414 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000415 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000416static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000417static void
418raise_exception(int e)
419{
420#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000421 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000422 abort();
423#endif
424 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000425 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000426 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000427}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000428#if DEBUG
429#define raise_exception(e) do { \
430 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
431 raise_exception(e); \
432} while (0)
433#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000434
435/*
436 * Called from trap.c when a SIGINT is received. (If the user specifies
437 * that SIGINT is to be trapped or ignored using the trap builtin, then
438 * this routine is not called.) Suppressint is nonzero when interrupts
439 * are held using the INT_OFF macro. (The test for iflag is just
440 * defensive programming.)
441 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000442static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000443static void
444raise_interrupt(void)
445{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000446 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000447
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200448 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000449 /* Signal is not automatically unmasked after it is raised,
450 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000451 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200452 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000453
Denis Vlasenko4b875702009-03-19 13:30:04 +0000454 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000455 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
456 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000457 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000458 signal(SIGINT, SIG_DFL);
459 raise(SIGINT);
460 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000461 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000462 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000463 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000464 /* NOTREACHED */
465}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000466#if DEBUG
467#define raise_interrupt() do { \
468 TRACE(("raising interrupt on line %d\n", __LINE__)); \
469 raise_interrupt(); \
470} while (0)
471#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000472
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000473static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000474int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000475{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000476 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200477 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000478 raise_interrupt();
479 }
480}
481#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000482static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000483force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000484{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000485 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200486 suppress_int = 0;
487 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000488 raise_interrupt();
489}
490#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000491
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200492#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000493
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000494#define RESTORE_INT(v) do { \
495 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200496 suppress_int = (v); \
497 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000498 raise_interrupt(); \
499} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000500
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000501
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000502/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000503
Eric Andersenc470f442003-07-28 09:56:35 +0000504static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000505outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000506{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000507 INT_OFF;
508 fputs(p, file);
509 INT_ON;
510}
511
512static void
513flush_stdout_stderr(void)
514{
515 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100516 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000517 INT_ON;
518}
519
520static void
521outcslow(int c, FILE *dest)
522{
523 INT_OFF;
524 putc(c, dest);
525 fflush(dest);
526 INT_ON;
527}
528
529static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
530static int
531out1fmt(const char *fmt, ...)
532{
533 va_list ap;
534 int r;
535
536 INT_OFF;
537 va_start(ap, fmt);
538 r = vprintf(fmt, ap);
539 va_end(ap);
540 INT_ON;
541 return r;
542}
543
544static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
545static int
546fmtstr(char *outbuf, size_t length, const char *fmt, ...)
547{
548 va_list ap;
549 int ret;
550
551 va_start(ap, fmt);
552 INT_OFF;
553 ret = vsnprintf(outbuf, length, fmt, ap);
554 va_end(ap);
555 INT_ON;
556 return ret;
557}
558
559static void
560out1str(const char *p)
561{
562 outstr(p, stdout);
563}
564
565static void
566out2str(const char *p)
567{
568 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100569 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000570}
571
572
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000573/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000574
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000575/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100576#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200577#define CTLESC ((unsigned char)'\201') /* escape next character */
578#define CTLVAR ((unsigned char)'\202') /* variable defn */
579#define CTLENDVAR ((unsigned char)'\203')
580#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000581#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
582/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200583#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
584#define CTLENDARI ((unsigned char)'\207')
585#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100586#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000587
588/* variable substitution byte (follows CTLVAR) */
589#define VSTYPE 0x0f /* type of variable substitution */
590#define VSNUL 0x10 /* colon--treat the empty string as unset */
591#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
592
593/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000594#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
595#define VSMINUS 0x2 /* ${var-text} */
596#define VSPLUS 0x3 /* ${var+text} */
597#define VSQUESTION 0x4 /* ${var?message} */
598#define VSASSIGN 0x5 /* ${var=text} */
599#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
600#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
601#define VSTRIMLEFT 0x8 /* ${var#pattern} */
602#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
603#define VSLENGTH 0xa /* ${#var} */
604#if ENABLE_ASH_BASH_COMPAT
605#define VSSUBSTR 0xc /* ${var:position:length} */
606#define VSREPLACE 0xd /* ${var/pattern/replacement} */
607#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
608#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000609
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000610static const char dolatstr[] ALIGN1 = {
611 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
612};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000613
Denis Vlasenko559691a2008-10-05 18:39:31 +0000614#define NCMD 0
615#define NPIPE 1
616#define NREDIR 2
617#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000618#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000619#define NAND 5
620#define NOR 6
621#define NSEMI 7
622#define NIF 8
623#define NWHILE 9
624#define NUNTIL 10
625#define NFOR 11
626#define NCASE 12
627#define NCLIST 13
628#define NDEFUN 14
629#define NARG 15
630#define NTO 16
631#if ENABLE_ASH_BASH_COMPAT
632#define NTO2 17
633#endif
634#define NCLOBBER 18
635#define NFROM 19
636#define NFROMTO 20
637#define NAPPEND 21
638#define NTOFD 22
639#define NFROMFD 23
640#define NHERE 24
641#define NXHERE 25
642#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000643#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000644
645union node;
646
647struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000648 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000649 union node *assign;
650 union node *args;
651 union node *redirect;
652};
653
654struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000655 smallint type;
656 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000657 struct nodelist *cmdlist;
658};
659
660struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000661 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000662 union node *n;
663 union node *redirect;
664};
665
666struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000667 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000668 union node *ch1;
669 union node *ch2;
670};
671
672struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000673 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000674 union node *test;
675 union node *ifpart;
676 union node *elsepart;
677};
678
679struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000680 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000681 union node *args;
682 union node *body;
683 char *var;
684};
685
686struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000687 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000688 union node *expr;
689 union node *cases;
690};
691
692struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000693 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000694 union node *next;
695 union node *pattern;
696 union node *body;
697};
698
699struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000700 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000701 union node *next;
702 char *text;
703 struct nodelist *backquote;
704};
705
Denis Vlasenko559691a2008-10-05 18:39:31 +0000706/* nfile and ndup layout must match!
707 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
708 * that it is actually NTO2 (>&file), and change its type.
709 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000710struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000711 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000712 union node *next;
713 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000714 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000715 union node *fname;
716 char *expfname;
717};
718
719struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000720 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000721 union node *next;
722 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000723 int dupfd;
724 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000725 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000726};
727
728struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000729 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000730 union node *next;
731 int fd;
732 union node *doc;
733};
734
735struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000736 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000737 union node *com;
738};
739
740union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000741 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000742 struct ncmd ncmd;
743 struct npipe npipe;
744 struct nredir nredir;
745 struct nbinary nbinary;
746 struct nif nif;
747 struct nfor nfor;
748 struct ncase ncase;
749 struct nclist nclist;
750 struct narg narg;
751 struct nfile nfile;
752 struct ndup ndup;
753 struct nhere nhere;
754 struct nnot nnot;
755};
756
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200757/*
758 * NODE_EOF is returned by parsecmd when it encounters an end of file.
759 * It must be distinct from NULL.
760 */
761#define NODE_EOF ((union node *) -1L)
762
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000763struct nodelist {
764 struct nodelist *next;
765 union node *n;
766};
767
768struct funcnode {
769 int count;
770 union node n;
771};
772
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000773/*
774 * Free a parse tree.
775 */
776static void
777freefunc(struct funcnode *f)
778{
779 if (f && --f->count < 0)
780 free(f);
781}
782
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000783
784/* ============ Debugging output */
785
786#if DEBUG
787
788static FILE *tracefile;
789
790static void
791trace_printf(const char *fmt, ...)
792{
793 va_list va;
794
795 if (debug != 1)
796 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000797 if (DEBUG_TIME)
798 fprintf(tracefile, "%u ", (int) time(NULL));
799 if (DEBUG_PID)
800 fprintf(tracefile, "[%u] ", (int) getpid());
801 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200802 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000803 va_start(va, fmt);
804 vfprintf(tracefile, fmt, va);
805 va_end(va);
806}
807
808static void
809trace_vprintf(const char *fmt, va_list va)
810{
811 if (debug != 1)
812 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000813 if (DEBUG_TIME)
814 fprintf(tracefile, "%u ", (int) time(NULL));
815 if (DEBUG_PID)
816 fprintf(tracefile, "[%u] ", (int) getpid());
817 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200818 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000819 vfprintf(tracefile, fmt, va);
820}
821
822static void
823trace_puts(const char *s)
824{
825 if (debug != 1)
826 return;
827 fputs(s, tracefile);
828}
829
830static void
831trace_puts_quoted(char *s)
832{
833 char *p;
834 char c;
835
836 if (debug != 1)
837 return;
838 putc('"', tracefile);
839 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100840 switch ((unsigned char)*p) {
841 case '\n': c = 'n'; goto backslash;
842 case '\t': c = 't'; goto backslash;
843 case '\r': c = 'r'; goto backslash;
844 case '\"': c = '\"'; goto backslash;
845 case '\\': c = '\\'; goto backslash;
846 case CTLESC: c = 'e'; goto backslash;
847 case CTLVAR: c = 'v'; goto backslash;
848 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
849 case CTLBACKQ: c = 'q'; goto backslash;
850 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000851 backslash:
852 putc('\\', tracefile);
853 putc(c, tracefile);
854 break;
855 default:
856 if (*p >= ' ' && *p <= '~')
857 putc(*p, tracefile);
858 else {
859 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100860 putc((*p >> 6) & 03, tracefile);
861 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000862 putc(*p & 07, tracefile);
863 }
864 break;
865 }
866 }
867 putc('"', tracefile);
868}
869
870static void
871trace_puts_args(char **ap)
872{
873 if (debug != 1)
874 return;
875 if (!*ap)
876 return;
877 while (1) {
878 trace_puts_quoted(*ap);
879 if (!*++ap) {
880 putc('\n', tracefile);
881 break;
882 }
883 putc(' ', tracefile);
884 }
885}
886
887static void
888opentrace(void)
889{
890 char s[100];
891#ifdef O_APPEND
892 int flags;
893#endif
894
895 if (debug != 1) {
896 if (tracefile)
897 fflush(tracefile);
898 /* leave open because libedit might be using it */
899 return;
900 }
901 strcpy(s, "./trace");
902 if (tracefile) {
903 if (!freopen(s, "a", tracefile)) {
904 fprintf(stderr, "Can't re-open %s\n", s);
905 debug = 0;
906 return;
907 }
908 } else {
909 tracefile = fopen(s, "a");
910 if (tracefile == NULL) {
911 fprintf(stderr, "Can't open %s\n", s);
912 debug = 0;
913 return;
914 }
915 }
916#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000917 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000918 if (flags >= 0)
919 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
920#endif
921 setlinebuf(tracefile);
922 fputs("\nTracing started.\n", tracefile);
923}
924
925static void
926indent(int amount, char *pfx, FILE *fp)
927{
928 int i;
929
930 for (i = 0; i < amount; i++) {
931 if (pfx && i == amount - 1)
932 fputs(pfx, fp);
933 putc('\t', fp);
934 }
935}
936
937/* little circular references here... */
938static void shtree(union node *n, int ind, char *pfx, FILE *fp);
939
940static void
941sharg(union node *arg, FILE *fp)
942{
943 char *p;
944 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100945 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000946
947 if (arg->type != NARG) {
948 out1fmt("<node type %d>\n", arg->type);
949 abort();
950 }
951 bqlist = arg->narg.backquote;
952 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100953 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000954 case CTLESC:
955 putc(*++p, fp);
956 break;
957 case CTLVAR:
958 putc('$', fp);
959 putc('{', fp);
960 subtype = *++p;
961 if (subtype == VSLENGTH)
962 putc('#', fp);
963
964 while (*p != '=')
965 putc(*p++, fp);
966
967 if (subtype & VSNUL)
968 putc(':', fp);
969
970 switch (subtype & VSTYPE) {
971 case VSNORMAL:
972 putc('}', fp);
973 break;
974 case VSMINUS:
975 putc('-', fp);
976 break;
977 case VSPLUS:
978 putc('+', fp);
979 break;
980 case VSQUESTION:
981 putc('?', fp);
982 break;
983 case VSASSIGN:
984 putc('=', fp);
985 break;
986 case VSTRIMLEFT:
987 putc('#', fp);
988 break;
989 case VSTRIMLEFTMAX:
990 putc('#', fp);
991 putc('#', fp);
992 break;
993 case VSTRIMRIGHT:
994 putc('%', fp);
995 break;
996 case VSTRIMRIGHTMAX:
997 putc('%', fp);
998 putc('%', fp);
999 break;
1000 case VSLENGTH:
1001 break;
1002 default:
1003 out1fmt("<subtype %d>", subtype);
1004 }
1005 break;
1006 case CTLENDVAR:
1007 putc('}', fp);
1008 break;
1009 case CTLBACKQ:
1010 case CTLBACKQ|CTLQUOTE:
1011 putc('$', fp);
1012 putc('(', fp);
1013 shtree(bqlist->n, -1, NULL, fp);
1014 putc(')', fp);
1015 break;
1016 default:
1017 putc(*p, fp);
1018 break;
1019 }
1020 }
1021}
1022
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001023static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001024shcmd(union node *cmd, FILE *fp)
1025{
1026 union node *np;
1027 int first;
1028 const char *s;
1029 int dftfd;
1030
1031 first = 1;
1032 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001033 if (!first)
1034 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001035 sharg(np, fp);
1036 first = 0;
1037 }
1038 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001039 if (!first)
1040 putc(' ', fp);
1041 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001042 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001043 case NTO: s = ">>"+1; dftfd = 1; break;
1044 case NCLOBBER: s = ">|"; dftfd = 1; break;
1045 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001046#if ENABLE_ASH_BASH_COMPAT
1047 case NTO2:
1048#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001049 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001050 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001051 case NFROMFD: s = "<&"; break;
1052 case NFROMTO: s = "<>"; break;
1053 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001054 }
1055 if (np->nfile.fd != dftfd)
1056 fprintf(fp, "%d", np->nfile.fd);
1057 fputs(s, fp);
1058 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1059 fprintf(fp, "%d", np->ndup.dupfd);
1060 } else {
1061 sharg(np->nfile.fname, fp);
1062 }
1063 first = 0;
1064 }
1065}
1066
1067static void
1068shtree(union node *n, int ind, char *pfx, FILE *fp)
1069{
1070 struct nodelist *lp;
1071 const char *s;
1072
1073 if (n == NULL)
1074 return;
1075
1076 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001077
1078 if (n == NODE_EOF) {
1079 fputs("<EOF>", fp);
1080 return;
1081 }
1082
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001083 switch (n->type) {
1084 case NSEMI:
1085 s = "; ";
1086 goto binop;
1087 case NAND:
1088 s = " && ";
1089 goto binop;
1090 case NOR:
1091 s = " || ";
1092 binop:
1093 shtree(n->nbinary.ch1, ind, NULL, fp);
1094 /* if (ind < 0) */
1095 fputs(s, fp);
1096 shtree(n->nbinary.ch2, ind, NULL, fp);
1097 break;
1098 case NCMD:
1099 shcmd(n, fp);
1100 if (ind >= 0)
1101 putc('\n', fp);
1102 break;
1103 case NPIPE:
1104 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001105 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001106 if (lp->next)
1107 fputs(" | ", fp);
1108 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001109 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001110 fputs(" &", fp);
1111 if (ind >= 0)
1112 putc('\n', fp);
1113 break;
1114 default:
1115 fprintf(fp, "<node type %d>", n->type);
1116 if (ind >= 0)
1117 putc('\n', fp);
1118 break;
1119 }
1120}
1121
1122static void
1123showtree(union node *n)
1124{
1125 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001126 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001127}
1128
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001129#endif /* DEBUG */
1130
1131
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001132/* ============ Parser data */
1133
1134/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001135 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1136 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001137struct strlist {
1138 struct strlist *next;
1139 char *text;
1140};
1141
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001142struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001143
Denis Vlasenkob012b102007-02-19 22:43:01 +00001144struct strpush {
1145 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001146 char *prev_string;
1147 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001148#if ENABLE_ASH_ALIAS
1149 struct alias *ap; /* if push was associated with an alias */
1150#endif
1151 char *string; /* remember the string since it may change */
1152};
1153
1154struct parsefile {
1155 struct parsefile *prev; /* preceding file on stack */
1156 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001157 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001158 int left_in_line; /* number of chars left in this line */
1159 int left_in_buffer; /* number of chars left in this buffer past the line */
1160 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001161 char *buf; /* input buffer */
1162 struct strpush *strpush; /* for pushing strings at this level */
1163 struct strpush basestrpush; /* so pushing one is fast */
1164};
1165
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001166static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001167static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001168static int startlinno; /* line # where last token started */
1169static char *commandname; /* currently executing command */
1170static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001171static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001172
1173
1174/* ============ Message printing */
1175
1176static void
1177ash_vmsg(const char *msg, va_list ap)
1178{
1179 fprintf(stderr, "%s: ", arg0);
1180 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001181 if (strcmp(arg0, commandname))
1182 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001183 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001184 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001185 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001186 vfprintf(stderr, msg, ap);
1187 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001188}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001189
1190/*
1191 * Exverror is called to raise the error exception. If the second argument
1192 * is not NULL then error prints an error message using printf style
1193 * formatting. It then raises the error exception.
1194 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001195static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001196static void
1197ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001198{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001199#if DEBUG
1200 if (msg) {
1201 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1202 TRACEV((msg, ap));
1203 TRACE(("\") pid=%d\n", getpid()));
1204 } else
1205 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1206 if (msg)
1207#endif
1208 ash_vmsg(msg, ap);
1209
1210 flush_stdout_stderr();
1211 raise_exception(cond);
1212 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001213}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001214
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001215static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001216static void
1217ash_msg_and_raise_error(const char *msg, ...)
1218{
1219 va_list ap;
1220
1221 va_start(ap, msg);
1222 ash_vmsg_and_raise(EXERROR, msg, ap);
1223 /* NOTREACHED */
1224 va_end(ap);
1225}
1226
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001227static void raise_error_syntax(const char *) NORETURN;
1228static void
1229raise_error_syntax(const char *msg)
1230{
1231 ash_msg_and_raise_error("syntax error: %s", msg);
1232 /* NOTREACHED */
1233}
1234
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001235static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001236static void
1237ash_msg_and_raise(int cond, const char *msg, ...)
1238{
1239 va_list ap;
1240
1241 va_start(ap, msg);
1242 ash_vmsg_and_raise(cond, msg, ap);
1243 /* NOTREACHED */
1244 va_end(ap);
1245}
1246
1247/*
1248 * error/warning routines for external builtins
1249 */
1250static void
1251ash_msg(const char *fmt, ...)
1252{
1253 va_list ap;
1254
1255 va_start(ap, fmt);
1256 ash_vmsg(fmt, ap);
1257 va_end(ap);
1258}
1259
1260/*
1261 * Return a string describing an error. The returned string may be a
1262 * pointer to a static buffer that will be overwritten on the next call.
1263 * Action describes the operation that got the error.
1264 */
1265static const char *
1266errmsg(int e, const char *em)
1267{
1268 if (e == ENOENT || e == ENOTDIR) {
1269 return em;
1270 }
1271 return strerror(e);
1272}
1273
1274
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001275/* ============ Memory allocation */
1276
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001277#if 0
1278/* I consider these wrappers nearly useless:
1279 * ok, they return you to nearest exception handler, but
1280 * how much memory do you leak in the process, making
1281 * memory starvation worse?
1282 */
1283static void *
1284ckrealloc(void * p, size_t nbytes)
1285{
1286 p = realloc(p, nbytes);
1287 if (!p)
1288 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1289 return p;
1290}
1291
1292static void *
1293ckmalloc(size_t nbytes)
1294{
1295 return ckrealloc(NULL, nbytes);
1296}
1297
1298static void *
1299ckzalloc(size_t nbytes)
1300{
1301 return memset(ckmalloc(nbytes), 0, nbytes);
1302}
1303
1304static char *
1305ckstrdup(const char *s)
1306{
1307 char *p = strdup(s);
1308 if (!p)
1309 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1310 return p;
1311}
1312#else
1313/* Using bbox equivalents. They exit if out of memory */
1314# define ckrealloc xrealloc
1315# define ckmalloc xmalloc
1316# define ckzalloc xzalloc
1317# define ckstrdup xstrdup
1318#endif
1319
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001320/*
1321 * It appears that grabstackstr() will barf with such alignments
1322 * because stalloc() will return a string allocated in a new stackblock.
1323 */
1324#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1325enum {
1326 /* Most machines require the value returned from malloc to be aligned
1327 * in some way. The following macro will get this right
1328 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001329 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001330 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001331 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001332};
1333
1334struct stack_block {
1335 struct stack_block *prev;
1336 char space[MINSIZE];
1337};
1338
1339struct stackmark {
1340 struct stack_block *stackp;
1341 char *stacknxt;
1342 size_t stacknleft;
1343 struct stackmark *marknext;
1344};
1345
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001346
Denis Vlasenko01631112007-12-16 17:20:38 +00001347struct globals_memstack {
1348 struct stack_block *g_stackp; // = &stackbase;
1349 struct stackmark *markp;
1350 char *g_stacknxt; // = stackbase.space;
1351 char *sstrend; // = stackbase.space + MINSIZE;
1352 size_t g_stacknleft; // = MINSIZE;
1353 int herefd; // = -1;
1354 struct stack_block stackbase;
1355};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001356extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1357#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001358#define g_stackp (G_memstack.g_stackp )
1359#define markp (G_memstack.markp )
1360#define g_stacknxt (G_memstack.g_stacknxt )
1361#define sstrend (G_memstack.sstrend )
1362#define g_stacknleft (G_memstack.g_stacknleft)
1363#define herefd (G_memstack.herefd )
1364#define stackbase (G_memstack.stackbase )
1365#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001366 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1367 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001368 g_stackp = &stackbase; \
1369 g_stacknxt = stackbase.space; \
1370 g_stacknleft = MINSIZE; \
1371 sstrend = stackbase.space + MINSIZE; \
1372 herefd = -1; \
1373} while (0)
1374
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001375
Denis Vlasenko01631112007-12-16 17:20:38 +00001376#define stackblock() ((void *)g_stacknxt)
1377#define stackblocksize() g_stacknleft
1378
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001379/*
1380 * Parse trees for commands are allocated in lifo order, so we use a stack
1381 * to make this more efficient, and also to avoid all sorts of exception
1382 * handling code to handle interrupts in the middle of a parse.
1383 *
1384 * The size 504 was chosen because the Ultrix malloc handles that size
1385 * well.
1386 */
1387static void *
1388stalloc(size_t nbytes)
1389{
1390 char *p;
1391 size_t aligned;
1392
1393 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001394 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001395 size_t len;
1396 size_t blocksize;
1397 struct stack_block *sp;
1398
1399 blocksize = aligned;
1400 if (blocksize < MINSIZE)
1401 blocksize = MINSIZE;
1402 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1403 if (len < blocksize)
1404 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1405 INT_OFF;
1406 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001407 sp->prev = g_stackp;
1408 g_stacknxt = sp->space;
1409 g_stacknleft = blocksize;
1410 sstrend = g_stacknxt + blocksize;
1411 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001412 INT_ON;
1413 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001414 p = g_stacknxt;
1415 g_stacknxt += aligned;
1416 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001417 return p;
1418}
1419
Denis Vlasenko597906c2008-02-20 16:38:54 +00001420static void *
1421stzalloc(size_t nbytes)
1422{
1423 return memset(stalloc(nbytes), 0, nbytes);
1424}
1425
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001426static void
1427stunalloc(void *p)
1428{
1429#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001430 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001431 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001432 abort();
1433 }
1434#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001435 g_stacknleft += g_stacknxt - (char *)p;
1436 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001437}
1438
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001439/*
1440 * Like strdup but works with the ash stack.
1441 */
1442static char *
1443ststrdup(const char *p)
1444{
1445 size_t len = strlen(p) + 1;
1446 return memcpy(stalloc(len), p, len);
1447}
1448
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001449static void
1450setstackmark(struct stackmark *mark)
1451{
Denis Vlasenko01631112007-12-16 17:20:38 +00001452 mark->stackp = g_stackp;
1453 mark->stacknxt = g_stacknxt;
1454 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001455 mark->marknext = markp;
1456 markp = mark;
1457}
1458
1459static void
1460popstackmark(struct stackmark *mark)
1461{
1462 struct stack_block *sp;
1463
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001464 if (!mark->stackp)
1465 return;
1466
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001467 INT_OFF;
1468 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001469 while (g_stackp != mark->stackp) {
1470 sp = g_stackp;
1471 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001472 free(sp);
1473 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001474 g_stacknxt = mark->stacknxt;
1475 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001476 sstrend = mark->stacknxt + mark->stacknleft;
1477 INT_ON;
1478}
1479
1480/*
1481 * When the parser reads in a string, it wants to stick the string on the
1482 * stack and only adjust the stack pointer when it knows how big the
1483 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1484 * of space on top of the stack and stackblocklen returns the length of
1485 * this block. Growstackblock will grow this space by at least one byte,
1486 * possibly moving it (like realloc). Grabstackblock actually allocates the
1487 * part of the block that has been used.
1488 */
1489static void
1490growstackblock(void)
1491{
1492 size_t newlen;
1493
Denis Vlasenko01631112007-12-16 17:20:38 +00001494 newlen = g_stacknleft * 2;
1495 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001496 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1497 if (newlen < 128)
1498 newlen += 128;
1499
Denis Vlasenko01631112007-12-16 17:20:38 +00001500 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001501 struct stack_block *oldstackp;
1502 struct stackmark *xmark;
1503 struct stack_block *sp;
1504 struct stack_block *prevstackp;
1505 size_t grosslen;
1506
1507 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001508 oldstackp = g_stackp;
1509 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001510 prevstackp = sp->prev;
1511 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1512 sp = ckrealloc(sp, grosslen);
1513 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001514 g_stackp = sp;
1515 g_stacknxt = sp->space;
1516 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001517 sstrend = sp->space + newlen;
1518
1519 /*
1520 * Stack marks pointing to the start of the old block
1521 * must be relocated to point to the new block
1522 */
1523 xmark = markp;
1524 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001525 xmark->stackp = g_stackp;
1526 xmark->stacknxt = g_stacknxt;
1527 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001528 xmark = xmark->marknext;
1529 }
1530 INT_ON;
1531 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001532 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001533 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001534 char *p = stalloc(newlen);
1535
1536 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001537 g_stacknxt = memcpy(p, oldspace, oldlen);
1538 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001539 }
1540}
1541
1542static void
1543grabstackblock(size_t len)
1544{
1545 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001546 g_stacknxt += len;
1547 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001548}
1549
1550/*
1551 * The following routines are somewhat easier to use than the above.
1552 * The user declares a variable of type STACKSTR, which may be declared
1553 * to be a register. The macro STARTSTACKSTR initializes things. Then
1554 * the user uses the macro STPUTC to add characters to the string. In
1555 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1556 * grown as necessary. When the user is done, she can just leave the
1557 * string there and refer to it using stackblock(). Or she can allocate
1558 * the space for it using grabstackstr(). If it is necessary to allow
1559 * someone else to use the stack temporarily and then continue to grow
1560 * the string, the user should use grabstack to allocate the space, and
1561 * then call ungrabstr(p) to return to the previous mode of operation.
1562 *
1563 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1564 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1565 * is space for at least one character.
1566 */
1567static void *
1568growstackstr(void)
1569{
1570 size_t len = stackblocksize();
1571 if (herefd >= 0 && len >= 1024) {
1572 full_write(herefd, stackblock(), len);
1573 return stackblock();
1574 }
1575 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001576 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001577}
1578
1579/*
1580 * Called from CHECKSTRSPACE.
1581 */
1582static char *
1583makestrspace(size_t newlen, char *p)
1584{
Denis Vlasenko01631112007-12-16 17:20:38 +00001585 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001586 size_t size = stackblocksize();
1587
1588 for (;;) {
1589 size_t nleft;
1590
1591 size = stackblocksize();
1592 nleft = size - len;
1593 if (nleft >= newlen)
1594 break;
1595 growstackblock();
1596 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001597 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001598}
1599
1600static char *
1601stack_nputstr(const char *s, size_t n, char *p)
1602{
1603 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001604 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001605 return p;
1606}
1607
1608static char *
1609stack_putstr(const char *s, char *p)
1610{
1611 return stack_nputstr(s, strlen(s), p);
1612}
1613
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001614static char *
1615_STPUTC(int c, char *p)
1616{
1617 if (p == sstrend)
1618 p = growstackstr();
1619 *p++ = c;
1620 return p;
1621}
1622
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001623#define STARTSTACKSTR(p) ((p) = stackblock())
1624#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001625#define CHECKSTRSPACE(n, p) do { \
1626 char *q = (p); \
1627 size_t l = (n); \
1628 size_t m = sstrend - q; \
1629 if (l > m) \
1630 (p) = makestrspace(l, q); \
1631} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001632#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001633#define STACKSTRNUL(p) do { \
1634 if ((p) == sstrend) \
1635 (p) = growstackstr(); \
1636 *(p) = '\0'; \
1637} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001638#define STUNPUTC(p) (--(p))
1639#define STTOPC(p) ((p)[-1])
1640#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001641
1642#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001643#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001644#define stackstrend() ((void *)sstrend)
1645
1646
1647/* ============ String helpers */
1648
1649/*
1650 * prefix -- see if pfx is a prefix of string.
1651 */
1652static char *
1653prefix(const char *string, const char *pfx)
1654{
1655 while (*pfx) {
1656 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001657 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001658 }
1659 return (char *) string;
1660}
1661
1662/*
1663 * Check for a valid number. This should be elsewhere.
1664 */
1665static int
1666is_number(const char *p)
1667{
1668 do {
1669 if (!isdigit(*p))
1670 return 0;
1671 } while (*++p != '\0');
1672 return 1;
1673}
1674
1675/*
1676 * Convert a string of digits to an integer, printing an error message on
1677 * failure.
1678 */
1679static int
1680number(const char *s)
1681{
1682 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001683 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001684 return atoi(s);
1685}
1686
1687/*
1688 * Produce a possibly single quoted string suitable as input to the shell.
1689 * The return string is allocated on the stack.
1690 */
1691static char *
1692single_quote(const char *s)
1693{
1694 char *p;
1695
1696 STARTSTACKSTR(p);
1697
1698 do {
1699 char *q;
1700 size_t len;
1701
1702 len = strchrnul(s, '\'') - s;
1703
1704 q = p = makestrspace(len + 3, p);
1705
1706 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001707 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001708 *q++ = '\'';
1709 s += len;
1710
1711 STADJUST(q - p, p);
1712
Denys Vlasenkocd716832009-11-28 22:14:02 +01001713 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001714 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001715 len = 0;
1716 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001717
1718 q = p = makestrspace(len + 3, p);
1719
1720 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001721 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001722 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723
1724 STADJUST(q - p, p);
1725 } while (*s);
1726
Denys Vlasenkocd716832009-11-28 22:14:02 +01001727 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001728
1729 return stackblock();
1730}
1731
1732
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001733/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734
1735static char **argptr; /* argument list for builtin commands */
1736static char *optionarg; /* set by nextopt (like getopt) */
1737static char *optptr; /* used by nextopt */
1738
1739/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001740 * XXX - should get rid of. Have all builtins use getopt(3).
1741 * The library getopt must have the BSD extension static variable
1742 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001744 * Standard option processing (a la getopt) for builtin routines.
1745 * The only argument that is passed to nextopt is the option string;
1746 * the other arguments are unnecessary. It returns the character,
1747 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748 */
1749static int
1750nextopt(const char *optstring)
1751{
1752 char *p;
1753 const char *q;
1754 char c;
1755
1756 p = optptr;
1757 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001758 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001760 if (p == NULL)
1761 return '\0';
1762 if (*p != '-')
1763 return '\0';
1764 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765 return '\0';
1766 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001767 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001768 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001769 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001770 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001771 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001773 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001775 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776 if (*++q == ':')
1777 q++;
1778 }
1779 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001780 if (*p == '\0') {
1781 p = *argptr++;
1782 if (p == NULL)
1783 ash_msg_and_raise_error("no arg for -%c option", c);
1784 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785 optionarg = p;
1786 p = NULL;
1787 }
1788 optptr = p;
1789 return c;
1790}
1791
1792
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001793/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001794
Denis Vlasenko01631112007-12-16 17:20:38 +00001795/*
1796 * The parsefile structure pointed to by the global variable parsefile
1797 * contains information about the current file being read.
1798 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001799struct shparam {
1800 int nparam; /* # of positional parameters (without $0) */
1801#if ENABLE_ASH_GETOPTS
1802 int optind; /* next parameter to be processed by getopts */
1803 int optoff; /* used by getopts */
1804#endif
1805 unsigned char malloced; /* if parameter list dynamically allocated */
1806 char **p; /* parameter list */
1807};
1808
1809/*
1810 * Free the list of positional parameters.
1811 */
1812static void
1813freeparam(volatile struct shparam *param)
1814{
Denis Vlasenko01631112007-12-16 17:20:38 +00001815 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001816 char **ap, **ap1;
1817 ap = ap1 = param->p;
1818 while (*ap)
1819 free(*ap++);
1820 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001821 }
1822}
1823
1824#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001825static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001826#endif
1827
1828struct var {
1829 struct var *next; /* next entry in hash list */
1830 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001831 const char *var_text; /* name=value */
1832 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001833 /* the variable gets set/unset */
1834};
1835
1836struct localvar {
1837 struct localvar *next; /* next local variable in list */
1838 struct var *vp; /* the variable that was made local */
1839 int flags; /* saved flags */
1840 const char *text; /* saved text */
1841};
1842
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001843/* flags */
1844#define VEXPORT 0x01 /* variable is exported */
1845#define VREADONLY 0x02 /* variable cannot be modified */
1846#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1847#define VTEXTFIXED 0x08 /* text is statically allocated */
1848#define VSTACK 0x10 /* text is allocated on the stack */
1849#define VUNSET 0x20 /* the variable is not set */
1850#define VNOFUNC 0x40 /* don't call the callback function */
1851#define VNOSET 0x80 /* do not set variable - just readonly test */
1852#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001853#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001854# define VDYNAMIC 0x200 /* dynamic variable */
1855#else
1856# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001857#endif
1858
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001859
Denis Vlasenko01631112007-12-16 17:20:38 +00001860/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001861#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001862static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001863change_lc_all(const char *value)
1864{
1865 if (value && *value != '\0')
1866 setlocale(LC_ALL, value);
1867}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001868static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001869change_lc_ctype(const char *value)
1870{
1871 if (value && *value != '\0')
1872 setlocale(LC_CTYPE, value);
1873}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001874#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001875#if ENABLE_ASH_MAIL
1876static void chkmail(void);
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001877static void changemail(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001878#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001879static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001880#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001881static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001882#endif
1883
Denis Vlasenko01631112007-12-16 17:20:38 +00001884static const struct {
1885 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001886 const char *var_text;
1887 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001888} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001889 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001890#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001891 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1892 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001893#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001894 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1895 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1896 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1897 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001898#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001899 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001900#endif
1901#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001902 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001903#endif
1904#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001905 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1906 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001907#endif
1908#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001909 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001910#endif
1911};
1912
Denis Vlasenko0b769642008-07-24 07:54:57 +00001913struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001914
1915struct globals_var {
1916 struct shparam shellparam; /* $@ current positional parameters */
1917 struct redirtab *redirlist;
1918 int g_nullredirs;
1919 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1920 struct var *vartab[VTABSIZE];
1921 struct var varinit[ARRAY_SIZE(varinit_data)];
1922};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001923extern struct globals_var *const ash_ptr_to_globals_var;
1924#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001925#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001926//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001927#define g_nullredirs (G_var.g_nullredirs )
1928#define preverrout_fd (G_var.preverrout_fd)
1929#define vartab (G_var.vartab )
1930#define varinit (G_var.varinit )
1931#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001932 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001933 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1934 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001935 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001936 varinit[i].flags = varinit_data[i].flags; \
1937 varinit[i].var_text = varinit_data[i].var_text; \
1938 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001939 } \
1940} while (0)
1941
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001942#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001943#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001944# define vmail (&vifs)[1]
1945# define vmpath (&vmail)[1]
1946# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001947#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001948# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001949#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001950#define vps1 (&vpath)[1]
1951#define vps2 (&vps1)[1]
1952#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001953#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001954# define voptind (&vps4)[1]
1955# if ENABLE_ASH_RANDOM_SUPPORT
1956# define vrandom (&voptind)[1]
1957# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001958#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001959# if ENABLE_ASH_RANDOM_SUPPORT
1960# define vrandom (&vps4)[1]
1961# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001962#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001963
1964/*
1965 * The following macros access the values of the above variables.
1966 * They have to skip over the name. They return the null string
1967 * for unset variables.
1968 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001969#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001971#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001972# define mailval() (vmail.var_text + 5)
1973# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001974# define mpathset() ((vmpath.flags & VUNSET) == 0)
1975#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001976#define pathval() (vpath.var_text + 5)
1977#define ps1val() (vps1.var_text + 4)
1978#define ps2val() (vps2.var_text + 4)
1979#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001980#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001981# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001982#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001983
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001985#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1986#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1987
Denis Vlasenko01631112007-12-16 17:20:38 +00001988#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001989static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001990getoptsreset(const char *value)
1991{
1992 shellparam.optind = number(value);
1993 shellparam.optoff = -1;
1994}
1995#endif
1996
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001997/*
1998 * Return of a legal variable name (a letter or underscore followed by zero or
1999 * more letters, underscores, and digits).
2000 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002001static char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002002endofname(const char *name)
2003{
2004 char *p;
2005
2006 p = (char *) name;
2007 if (!is_name(*p))
2008 return p;
2009 while (*++p) {
2010 if (!is_in_name(*p))
2011 break;
2012 }
2013 return p;
2014}
2015
2016/*
2017 * Compares two strings up to the first = or '\0'. The first
2018 * string must be terminated by '='; the second may be terminated by
2019 * either '=' or '\0'.
2020 */
2021static int
2022varcmp(const char *p, const char *q)
2023{
2024 int c, d;
2025
2026 while ((c = *p) == (d = *q)) {
2027 if (!c || c == '=')
2028 goto out;
2029 p++;
2030 q++;
2031 }
2032 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002033 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002034 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002035 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002036 out:
2037 return c - d;
2038}
2039
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002040/*
2041 * Find the appropriate entry in the hash table from the name.
2042 */
2043static struct var **
2044hashvar(const char *p)
2045{
2046 unsigned hashval;
2047
2048 hashval = ((unsigned char) *p) << 4;
2049 while (*p && *p != '=')
2050 hashval += (unsigned char) *p++;
2051 return &vartab[hashval % VTABSIZE];
2052}
2053
2054static int
2055vpcmp(const void *a, const void *b)
2056{
2057 return varcmp(*(const char **)a, *(const char **)b);
2058}
2059
2060/*
2061 * This routine initializes the builtin variables.
2062 */
2063static void
2064initvar(void)
2065{
2066 struct var *vp;
2067 struct var *end;
2068 struct var **vpp;
2069
2070 /*
2071 * PS1 depends on uid
2072 */
2073#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002074 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002075#else
2076 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002077 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002078#endif
2079 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002080 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002081 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002082 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002083 vp->next = *vpp;
2084 *vpp = vp;
2085 } while (++vp < end);
2086}
2087
2088static struct var **
2089findvar(struct var **vpp, const char *name)
2090{
2091 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002092 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002093 break;
2094 }
2095 }
2096 return vpp;
2097}
2098
2099/*
2100 * Find the value of a variable. Returns NULL if not set.
2101 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002102static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002103lookupvar(const char *name)
2104{
2105 struct var *v;
2106
2107 v = *findvar(hashvar(name), name);
2108 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002109#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002110 /*
2111 * Dynamic variables are implemented roughly the same way they are
2112 * in bash. Namely, they're "special" so long as they aren't unset.
2113 * As soon as they're unset, they're no longer dynamic, and dynamic
2114 * lookup will no longer happen at that point. -- PFM.
2115 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002116 if (v->flags & VDYNAMIC)
2117 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002118#endif
2119 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002120 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002121 }
2122 return NULL;
2123}
2124
2125/*
2126 * Search the environment of a builtin command.
2127 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002128static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002129bltinlookup(const char *name)
2130{
2131 struct strlist *sp;
2132
2133 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002134 if (varcmp(sp->text, name) == 0)
2135 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002136 }
2137 return lookupvar(name);
2138}
2139
2140/*
2141 * Same as setvar except that the variable and value are passed in
2142 * the first argument as name=value. Since the first argument will
2143 * be actually stored in the table, it should not be a string that
2144 * will go away.
2145 * Called with interrupts off.
2146 */
2147static void
2148setvareq(char *s, int flags)
2149{
2150 struct var *vp, **vpp;
2151
2152 vpp = hashvar(s);
2153 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2154 vp = *findvar(vpp, s);
2155 if (vp) {
2156 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2157 const char *n;
2158
2159 if (flags & VNOSAVE)
2160 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002161 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002162 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2163 }
2164
2165 if (flags & VNOSET)
2166 return;
2167
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002168 if (vp->var_func && !(flags & VNOFUNC))
2169 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002170
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002171 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2172 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002173
2174 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2175 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002176 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002177 if (flags & VNOSET)
2178 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002179 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002180 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002181 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002182 *vpp = vp;
2183 }
2184 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2185 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002186 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002187 vp->flags = flags;
2188}
2189
2190/*
2191 * Set the value of a variable. The flags argument is ored with the
2192 * flags of the variable. If val is NULL, the variable is unset.
2193 */
2194static void
2195setvar(const char *name, const char *val, int flags)
2196{
2197 char *p, *q;
2198 size_t namelen;
2199 char *nameeq;
2200 size_t vallen;
2201
2202 q = endofname(name);
2203 p = strchrnul(q, '=');
2204 namelen = p - name;
2205 if (!namelen || p != q)
2206 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2207 vallen = 0;
2208 if (val == NULL) {
2209 flags |= VUNSET;
2210 } else {
2211 vallen = strlen(val);
2212 }
2213 INT_OFF;
2214 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002215 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002216 if (val) {
2217 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002218 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002219 }
2220 *p = '\0';
2221 setvareq(nameeq, flags | VNOSAVE);
2222 INT_ON;
2223}
2224
Denys Vlasenko03dad222010-01-12 23:29:57 +01002225static void FAST_FUNC
2226setvar2(const char *name, const char *val)
2227{
2228 setvar(name, val, 0);
2229}
2230
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231#if ENABLE_ASH_GETOPTS
2232/*
2233 * Safe version of setvar, returns 1 on success 0 on failure.
2234 */
2235static int
2236setvarsafe(const char *name, const char *val, int flags)
2237{
2238 int err;
2239 volatile int saveint;
2240 struct jmploc *volatile savehandler = exception_handler;
2241 struct jmploc jmploc;
2242
2243 SAVE_INT(saveint);
2244 if (setjmp(jmploc.loc))
2245 err = 1;
2246 else {
2247 exception_handler = &jmploc;
2248 setvar(name, val, flags);
2249 err = 0;
2250 }
2251 exception_handler = savehandler;
2252 RESTORE_INT(saveint);
2253 return err;
2254}
2255#endif
2256
2257/*
2258 * Unset the specified variable.
2259 */
2260static int
2261unsetvar(const char *s)
2262{
2263 struct var **vpp;
2264 struct var *vp;
2265 int retval;
2266
2267 vpp = findvar(hashvar(s), s);
2268 vp = *vpp;
2269 retval = 2;
2270 if (vp) {
2271 int flags = vp->flags;
2272
2273 retval = 1;
2274 if (flags & VREADONLY)
2275 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002276#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002277 vp->flags &= ~VDYNAMIC;
2278#endif
2279 if (flags & VUNSET)
2280 goto ok;
2281 if ((flags & VSTRFIXED) == 0) {
2282 INT_OFF;
2283 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002284 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002285 *vpp = vp->next;
2286 free(vp);
2287 INT_ON;
2288 } else {
2289 setvar(s, 0, 0);
2290 vp->flags &= ~VEXPORT;
2291 }
2292 ok:
2293 retval = 0;
2294 }
2295 out:
2296 return retval;
2297}
2298
2299/*
2300 * Process a linked list of variable assignments.
2301 */
2302static void
2303listsetvar(struct strlist *list_set_var, int flags)
2304{
2305 struct strlist *lp = list_set_var;
2306
2307 if (!lp)
2308 return;
2309 INT_OFF;
2310 do {
2311 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002312 lp = lp->next;
2313 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002314 INT_ON;
2315}
2316
2317/*
2318 * Generate a list of variables satisfying the given conditions.
2319 */
2320static char **
2321listvars(int on, int off, char ***end)
2322{
2323 struct var **vpp;
2324 struct var *vp;
2325 char **ep;
2326 int mask;
2327
2328 STARTSTACKSTR(ep);
2329 vpp = vartab;
2330 mask = on | off;
2331 do {
2332 for (vp = *vpp; vp; vp = vp->next) {
2333 if ((vp->flags & mask) == on) {
2334 if (ep == stackstrend())
2335 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002336 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002337 }
2338 }
2339 } while (++vpp < vartab + VTABSIZE);
2340 if (ep == stackstrend())
2341 ep = growstackstr();
2342 if (end)
2343 *end = ep;
2344 *ep++ = NULL;
2345 return grabstackstr(ep);
2346}
2347
2348
2349/* ============ Path search helper
2350 *
2351 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002352 * of the path before the first call; path_advance will update
2353 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002354 * the possible path expansions in sequence. If an option (indicated by
2355 * a percent sign) appears in the path entry then the global variable
2356 * pathopt will be set to point to it; otherwise pathopt will be set to
2357 * NULL.
2358 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002359static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002360
2361static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002362path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002363{
2364 const char *p;
2365 char *q;
2366 const char *start;
2367 size_t len;
2368
2369 if (*path == NULL)
2370 return NULL;
2371 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002372 for (p = start; *p && *p != ':' && *p != '%'; p++)
2373 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002374 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2375 while (stackblocksize() < len)
2376 growstackblock();
2377 q = stackblock();
2378 if (p != start) {
2379 memcpy(q, start, p - start);
2380 q += p - start;
2381 *q++ = '/';
2382 }
2383 strcpy(q, name);
2384 pathopt = NULL;
2385 if (*p == '%') {
2386 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002387 while (*p && *p != ':')
2388 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002389 }
2390 if (*p == ':')
2391 *path = p + 1;
2392 else
2393 *path = NULL;
2394 return stalloc(len);
2395}
2396
2397
2398/* ============ Prompt */
2399
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002400static smallint doprompt; /* if set, prompt the user */
2401static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002402
2403#if ENABLE_FEATURE_EDITING
2404static line_input_t *line_input_state;
2405static const char *cmdedit_prompt;
2406static void
2407putprompt(const char *s)
2408{
2409 if (ENABLE_ASH_EXPAND_PRMT) {
2410 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002411 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002412 return;
2413 }
2414 cmdedit_prompt = s;
2415}
2416#else
2417static void
2418putprompt(const char *s)
2419{
2420 out2str(s);
2421}
2422#endif
2423
2424#if ENABLE_ASH_EXPAND_PRMT
2425/* expandstr() needs parsing machinery, so it is far away ahead... */
2426static const char *expandstr(const char *ps);
2427#else
2428#define expandstr(s) s
2429#endif
2430
2431static void
2432setprompt(int whichprompt)
2433{
2434 const char *prompt;
2435#if ENABLE_ASH_EXPAND_PRMT
2436 struct stackmark smark;
2437#endif
2438
2439 needprompt = 0;
2440
2441 switch (whichprompt) {
2442 case 1:
2443 prompt = ps1val();
2444 break;
2445 case 2:
2446 prompt = ps2val();
2447 break;
2448 default: /* 0 */
2449 prompt = nullstr;
2450 }
2451#if ENABLE_ASH_EXPAND_PRMT
2452 setstackmark(&smark);
2453 stalloc(stackblocksize());
2454#endif
2455 putprompt(expandstr(prompt));
2456#if ENABLE_ASH_EXPAND_PRMT
2457 popstackmark(&smark);
2458#endif
2459}
2460
2461
2462/* ============ The cd and pwd commands */
2463
2464#define CD_PHYSICAL 1
2465#define CD_PRINT 2
2466
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002467static int
2468cdopt(void)
2469{
2470 int flags = 0;
2471 int i, j;
2472
2473 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002474 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002475 if (i != j) {
2476 flags ^= CD_PHYSICAL;
2477 j = i;
2478 }
2479 }
2480
2481 return flags;
2482}
2483
2484/*
2485 * Update curdir (the name of the current directory) in response to a
2486 * cd command.
2487 */
2488static const char *
2489updatepwd(const char *dir)
2490{
2491 char *new;
2492 char *p;
2493 char *cdcomppath;
2494 const char *lim;
2495
2496 cdcomppath = ststrdup(dir);
2497 STARTSTACKSTR(new);
2498 if (*dir != '/') {
2499 if (curdir == nullstr)
2500 return 0;
2501 new = stack_putstr(curdir, new);
2502 }
2503 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002504 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002505 if (*dir != '/') {
2506 if (new[-1] != '/')
2507 USTPUTC('/', new);
2508 if (new > lim && *lim == '/')
2509 lim++;
2510 } else {
2511 USTPUTC('/', new);
2512 cdcomppath++;
2513 if (dir[1] == '/' && dir[2] != '/') {
2514 USTPUTC('/', new);
2515 cdcomppath++;
2516 lim++;
2517 }
2518 }
2519 p = strtok(cdcomppath, "/");
2520 while (p) {
2521 switch (*p) {
2522 case '.':
2523 if (p[1] == '.' && p[2] == '\0') {
2524 while (new > lim) {
2525 STUNPUTC(new);
2526 if (new[-1] == '/')
2527 break;
2528 }
2529 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002530 }
2531 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002532 break;
2533 /* fall through */
2534 default:
2535 new = stack_putstr(p, new);
2536 USTPUTC('/', new);
2537 }
2538 p = strtok(0, "/");
2539 }
2540 if (new > lim)
2541 STUNPUTC(new);
2542 *new = 0;
2543 return stackblock();
2544}
2545
2546/*
2547 * Find out what the current directory is. If we already know the current
2548 * directory, this routine returns immediately.
2549 */
2550static char *
2551getpwd(void)
2552{
Denis Vlasenko01631112007-12-16 17:20:38 +00002553 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002554 return dir ? dir : nullstr;
2555}
2556
2557static void
2558setpwd(const char *val, int setold)
2559{
2560 char *oldcur, *dir;
2561
2562 oldcur = dir = curdir;
2563
2564 if (setold) {
2565 setvar("OLDPWD", oldcur, VEXPORT);
2566 }
2567 INT_OFF;
2568 if (physdir != nullstr) {
2569 if (physdir != oldcur)
2570 free(physdir);
2571 physdir = nullstr;
2572 }
2573 if (oldcur == val || !val) {
2574 char *s = getpwd();
2575 physdir = s;
2576 if (!val)
2577 dir = s;
2578 } else
2579 dir = ckstrdup(val);
2580 if (oldcur != dir && oldcur != nullstr) {
2581 free(oldcur);
2582 }
2583 curdir = dir;
2584 INT_ON;
2585 setvar("PWD", dir, VEXPORT);
2586}
2587
2588static void hashcd(void);
2589
2590/*
2591 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2592 * know that the current directory has changed.
2593 */
2594static int
2595docd(const char *dest, int flags)
2596{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002597 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002598 int err;
2599
2600 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2601
2602 INT_OFF;
2603 if (!(flags & CD_PHYSICAL)) {
2604 dir = updatepwd(dest);
2605 if (dir)
2606 dest = dir;
2607 }
2608 err = chdir(dest);
2609 if (err)
2610 goto out;
2611 setpwd(dir, 1);
2612 hashcd();
2613 out:
2614 INT_ON;
2615 return err;
2616}
2617
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002618static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002619cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002620{
2621 const char *dest;
2622 const char *path;
2623 const char *p;
2624 char c;
2625 struct stat statb;
2626 int flags;
2627
2628 flags = cdopt();
2629 dest = *argptr;
2630 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002631 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002632 else if (LONE_DASH(dest)) {
2633 dest = bltinlookup("OLDPWD");
2634 flags |= CD_PRINT;
2635 }
2636 if (!dest)
2637 dest = nullstr;
2638 if (*dest == '/')
2639 goto step7;
2640 if (*dest == '.') {
2641 c = dest[1];
2642 dotdot:
2643 switch (c) {
2644 case '\0':
2645 case '/':
2646 goto step6;
2647 case '.':
2648 c = dest[2];
2649 if (c != '.')
2650 goto dotdot;
2651 }
2652 }
2653 if (!*dest)
2654 dest = ".";
2655 path = bltinlookup("CDPATH");
2656 if (!path) {
2657 step6:
2658 step7:
2659 p = dest;
2660 goto docd;
2661 }
2662 do {
2663 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002664 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002665 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2666 if (c && c != ':')
2667 flags |= CD_PRINT;
2668 docd:
2669 if (!docd(p, flags))
2670 goto out;
2671 break;
2672 }
2673 } while (path);
2674 ash_msg_and_raise_error("can't cd to %s", dest);
2675 /* NOTREACHED */
2676 out:
2677 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002678 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002679 return 0;
2680}
2681
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002682static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002683pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002684{
2685 int flags;
2686 const char *dir = curdir;
2687
2688 flags = cdopt();
2689 if (flags) {
2690 if (physdir == nullstr)
2691 setpwd(dir, 0);
2692 dir = physdir;
2693 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002694 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002695 return 0;
2696}
2697
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002698
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002699/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002700
Denis Vlasenko834dee72008-10-07 09:18:30 +00002701
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002702#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002703
Eric Andersenc470f442003-07-28 09:56:35 +00002704/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002705#define CWORD 0 /* character is nothing special */
2706#define CNL 1 /* newline character */
2707#define CBACK 2 /* a backslash character */
2708#define CSQUOTE 3 /* single quote */
2709#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002710#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002711#define CBQUOTE 6 /* backwards single quote */
2712#define CVAR 7 /* a dollar sign */
2713#define CENDVAR 8 /* a '}' character */
2714#define CLP 9 /* a left paren in arithmetic */
2715#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002716#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002717#define CCTL 12 /* like CWORD, except it must be escaped */
2718#define CSPCL 13 /* these terminate a word */
2719#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002720
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002721#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002722#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002723# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002724#endif
2725
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002726#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002727
Mike Frysinger98c52642009-04-02 10:02:37 +00002728#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002729# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002730#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002731# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002732#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002733static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002734#if ENABLE_ASH_ALIAS
2735 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2736#endif
2737 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2738 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2739 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2740 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2741 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2742 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2743 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2744 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2745 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2746 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2747 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002748#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002749 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2750 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2751 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2752#endif
2753#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002754};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002755/* Constants below must match table above */
2756enum {
2757#if ENABLE_ASH_ALIAS
2758 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2759#endif
2760 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2761 CNL_CNL_CNL_CNL , /* 2 */
2762 CWORD_CCTL_CCTL_CWORD , /* 3 */
2763 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2764 CVAR_CVAR_CWORD_CVAR , /* 5 */
2765 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2766 CSPCL_CWORD_CWORD_CLP , /* 7 */
2767 CSPCL_CWORD_CWORD_CRP , /* 8 */
2768 CBACK_CBACK_CCTL_CBACK , /* 9 */
2769 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2770 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2771 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2772 CWORD_CWORD_CWORD_CWORD , /* 13 */
2773 CCTL_CCTL_CCTL_CCTL , /* 14 */
2774};
Eric Andersen2870d962001-07-02 17:27:21 +00002775
Denys Vlasenkocd716832009-11-28 22:14:02 +01002776/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2777 * caller must ensure proper cast on it if c is *char_ptr!
2778 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002779/* Values for syntax param */
2780#define BASESYNTAX 0 /* not in quotes */
2781#define DQSYNTAX 1 /* in double quotes */
2782#define SQSYNTAX 2 /* in single quotes */
2783#define ARISYNTAX 3 /* in arithmetic */
2784#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002785
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002786#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002787
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002788static int
2789SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002790{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002791 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002792# if ENABLE_ASH_ALIAS
2793 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002794 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2795 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2796 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2797 11, 3 /* "}~" */
2798 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002799# else
2800 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002801 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2802 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2803 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2804 10, 2 /* "}~" */
2805 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002806# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002807 const char *s;
2808 int indx;
2809
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002810 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002811 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002812# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002813 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002814 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002815 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002816# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002817 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002818 /* Cast is purely for paranoia here,
2819 * just in case someone passed signed char to us */
2820 if ((unsigned char)c >= CTL_FIRST
2821 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002822 ) {
2823 return CCTL;
2824 }
2825 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002826 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002827 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002828 indx = syntax_index_table[s - spec_symbls];
2829 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002830 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002831}
2832
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002833#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002834
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002835static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002836 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002837 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2847 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2848 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2870 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2871 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2872 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2874 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2876 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2877 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2878 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2879 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2880 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2882 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2883 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2884 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2885 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2896 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2897 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2898 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2899 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2900 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2901 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2929 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2930 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2931 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2934 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2962 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2963 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2964 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2965 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2966 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2967 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2968 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2969 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2970 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2971 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2972 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2973 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2974 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2975 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2976 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2977 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2978 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2979 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2980 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2981 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2982 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2983 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2986 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 150 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 151 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 152 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 153 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 154 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 155 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 156 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 157 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 158 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 159 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 160 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 161 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003093 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003094# if ENABLE_ASH_ALIAS
3095 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3096# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003097};
3098
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003099# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003100
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003101#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003102
Eric Andersen2870d962001-07-02 17:27:21 +00003103
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003104/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003105
Denis Vlasenko131ae172007-02-18 13:00:19 +00003106#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003107
3108#define ALIASINUSE 1
3109#define ALIASDEAD 2
3110
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003111struct alias {
3112 struct alias *next;
3113 char *name;
3114 char *val;
3115 int flag;
3116};
3117
Denis Vlasenko01631112007-12-16 17:20:38 +00003118
3119static struct alias **atab; // [ATABSIZE];
3120#define INIT_G_alias() do { \
3121 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3122} while (0)
3123
Eric Andersen2870d962001-07-02 17:27:21 +00003124
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003125static struct alias **
3126__lookupalias(const char *name) {
3127 unsigned int hashval;
3128 struct alias **app;
3129 const char *p;
3130 unsigned int ch;
3131
3132 p = name;
3133
3134 ch = (unsigned char)*p;
3135 hashval = ch << 4;
3136 while (ch) {
3137 hashval += ch;
3138 ch = (unsigned char)*++p;
3139 }
3140 app = &atab[hashval % ATABSIZE];
3141
3142 for (; *app; app = &(*app)->next) {
3143 if (strcmp(name, (*app)->name) == 0) {
3144 break;
3145 }
3146 }
3147
3148 return app;
3149}
3150
3151static struct alias *
3152lookupalias(const char *name, int check)
3153{
3154 struct alias *ap = *__lookupalias(name);
3155
3156 if (check && ap && (ap->flag & ALIASINUSE))
3157 return NULL;
3158 return ap;
3159}
3160
3161static struct alias *
3162freealias(struct alias *ap)
3163{
3164 struct alias *next;
3165
3166 if (ap->flag & ALIASINUSE) {
3167 ap->flag |= ALIASDEAD;
3168 return ap;
3169 }
3170
3171 next = ap->next;
3172 free(ap->name);
3173 free(ap->val);
3174 free(ap);
3175 return next;
3176}
Eric Andersencb57d552001-06-28 07:25:16 +00003177
Eric Andersenc470f442003-07-28 09:56:35 +00003178static void
3179setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003180{
3181 struct alias *ap, **app;
3182
3183 app = __lookupalias(name);
3184 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003185 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003186 if (ap) {
3187 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003188 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003189 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003190 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003191 ap->flag &= ~ALIASDEAD;
3192 } else {
3193 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003194 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003195 ap->name = ckstrdup(name);
3196 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003197 /*ap->flag = 0; - ckzalloc did it */
3198 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003199 *app = ap;
3200 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003201 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003202}
3203
Eric Andersenc470f442003-07-28 09:56:35 +00003204static int
3205unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003206{
Eric Andersencb57d552001-06-28 07:25:16 +00003207 struct alias **app;
3208
3209 app = __lookupalias(name);
3210
3211 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003212 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003213 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003214 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003215 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003216 }
3217
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003218 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003219}
3220
Eric Andersenc470f442003-07-28 09:56:35 +00003221static void
3222rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003223{
Eric Andersencb57d552001-06-28 07:25:16 +00003224 struct alias *ap, **app;
3225 int i;
3226
Denis Vlasenkob012b102007-02-19 22:43:01 +00003227 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003228 for (i = 0; i < ATABSIZE; i++) {
3229 app = &atab[i];
3230 for (ap = *app; ap; ap = *app) {
3231 *app = freealias(*app);
3232 if (ap == *app) {
3233 app = &ap->next;
3234 }
3235 }
3236 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003237 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003238}
3239
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003240static void
3241printalias(const struct alias *ap)
3242{
3243 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3244}
3245
Eric Andersencb57d552001-06-28 07:25:16 +00003246/*
3247 * TODO - sort output
3248 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003249static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003250aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003251{
3252 char *n, *v;
3253 int ret = 0;
3254 struct alias *ap;
3255
Denis Vlasenko68404f12008-03-17 09:00:54 +00003256 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003257 int i;
3258
Denis Vlasenko68404f12008-03-17 09:00:54 +00003259 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003260 for (ap = atab[i]; ap; ap = ap->next) {
3261 printalias(ap);
3262 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003263 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003264 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003265 }
3266 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003267 v = strchr(n+1, '=');
3268 if (v == NULL) { /* n+1: funny ksh stuff */
3269 ap = *__lookupalias(n);
3270 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003271 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003272 ret = 1;
3273 } else
3274 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003275 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003276 *v++ = '\0';
3277 setalias(n, v);
3278 }
3279 }
3280
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003281 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003282}
3283
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003284static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003285unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003286{
3287 int i;
3288
3289 while ((i = nextopt("a")) != '\0') {
3290 if (i == 'a') {
3291 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003292 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003293 }
3294 }
3295 for (i = 0; *argptr; argptr++) {
3296 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003297 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003298 i = 1;
3299 }
3300 }
3301
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003302 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003303}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003304
Denis Vlasenko131ae172007-02-18 13:00:19 +00003305#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003306
Eric Andersenc470f442003-07-28 09:56:35 +00003307
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003308/* ============ jobs.c */
3309
3310/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003311#define FORK_FG 0
3312#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003313#define FORK_NOJOB 2
3314
3315/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003316#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3317#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3318#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003319
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320/*
3321 * A job structure contains information about a job. A job is either a
3322 * single process or a set of processes contained in a pipeline. In the
3323 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3324 * array of pids.
3325 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003326struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003327 pid_t ps_pid; /* process id */
3328 int ps_status; /* last process status from wait() */
3329 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003330};
3331
3332struct job {
3333 struct procstat ps0; /* status of process */
3334 struct procstat *ps; /* status or processes when more than one */
3335#if JOBS
3336 int stopstatus; /* status of a stopped job */
3337#endif
3338 uint32_t
3339 nprocs: 16, /* number of processes */
3340 state: 8,
3341#define JOBRUNNING 0 /* at least one proc running */
3342#define JOBSTOPPED 1 /* all procs are stopped */
3343#define JOBDONE 2 /* all procs are completed */
3344#if JOBS
3345 sigint: 1, /* job was killed by SIGINT */
3346 jobctl: 1, /* job running under job control */
3347#endif
3348 waited: 1, /* true if this entry has been waited for */
3349 used: 1, /* true if this entry is in used */
3350 changed: 1; /* true if status has changed */
3351 struct job *prev_job; /* previous job */
3352};
3353
Denis Vlasenko68404f12008-03-17 09:00:54 +00003354static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003355static int forkshell(struct job *, union node *, int);
3356static int waitforjob(struct job *);
3357
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003358#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003359enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003360#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003361#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003362static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003363static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364#endif
3365
3366/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003367 * Ignore a signal.
3368 */
3369static void
3370ignoresig(int signo)
3371{
3372 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3373 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3374 /* No, need to do it */
3375 signal(signo, SIG_IGN);
3376 }
3377 sigmode[signo - 1] = S_HARD_IGN;
3378}
3379
3380/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003381 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003382 */
3383static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003384signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003385{
3386 gotsig[signo - 1] = 1;
3387
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003388 if (signo == SIGINT && !trap[SIGINT]) {
3389 if (!suppress_int) {
3390 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003391 raise_interrupt(); /* does not return */
3392 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003393 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003394 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003395 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003396 }
3397}
3398
3399/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003400 * Set the signal handler for the specified signal. The routine figures
3401 * out what it should be set to.
3402 */
3403static void
3404setsignal(int signo)
3405{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003406 char *t;
3407 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003408 struct sigaction act;
3409
3410 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003411 new_act = S_DFL;
3412 if (t != NULL) { /* trap for this sig is set */
3413 new_act = S_CATCH;
3414 if (t[0] == '\0') /* trap is "": ignore this sig */
3415 new_act = S_IGN;
3416 }
3417
3418 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003419 switch (signo) {
3420 case SIGINT:
3421 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003422 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003423 break;
3424 case SIGQUIT:
3425#if DEBUG
3426 if (debug)
3427 break;
3428#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003429 /* man bash:
3430 * "In all cases, bash ignores SIGQUIT. Non-builtin
3431 * commands run by bash have signal handlers
3432 * set to the values inherited by the shell
3433 * from its parent". */
3434 new_act = S_IGN;
3435 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003436 case SIGTERM:
3437 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003438 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003439 break;
3440#if JOBS
3441 case SIGTSTP:
3442 case SIGTTOU:
3443 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003444 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003445 break;
3446#endif
3447 }
3448 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003449//TODO: if !rootshell, we reset SIGQUIT to DFL,
3450//whereas we have to restore it to what shell got on entry
3451//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003452
3453 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003454 cur_act = *t;
3455 if (cur_act == 0) {
3456 /* current setting is not yet known */
3457 if (sigaction(signo, NULL, &act)) {
3458 /* pretend it worked; maybe we should give a warning,
3459 * but other shells don't. We don't alter sigmode,
3460 * so we retry every time.
3461 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003462 return;
3463 }
3464 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003465 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003466 if (mflag
3467 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3468 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003469 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003470 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003471 }
3472 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003473 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003474 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003475
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003476 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003477 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003478 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003479 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003480 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3481 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003482 break;
3483 case S_IGN:
3484 act.sa_handler = SIG_IGN;
3485 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003486 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003487 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003488
3489 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003490}
3491
3492/* mode flags for set_curjob */
3493#define CUR_DELETE 2
3494#define CUR_RUNNING 1
3495#define CUR_STOPPED 0
3496
3497/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003498#define DOWAIT_NONBLOCK WNOHANG
3499#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003500
3501#if JOBS
3502/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003503static int initialpgrp; //references:2
3504static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003505#endif
3506/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003507static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003508/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003509static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003510/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003511static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003512/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003513static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003514
3515static void
3516set_curjob(struct job *jp, unsigned mode)
3517{
3518 struct job *jp1;
3519 struct job **jpp, **curp;
3520
3521 /* first remove from list */
3522 jpp = curp = &curjob;
3523 do {
3524 jp1 = *jpp;
3525 if (jp1 == jp)
3526 break;
3527 jpp = &jp1->prev_job;
3528 } while (1);
3529 *jpp = jp1->prev_job;
3530
3531 /* Then re-insert in correct position */
3532 jpp = curp;
3533 switch (mode) {
3534 default:
3535#if DEBUG
3536 abort();
3537#endif
3538 case CUR_DELETE:
3539 /* job being deleted */
3540 break;
3541 case CUR_RUNNING:
3542 /* newly created job or backgrounded job,
3543 put after all stopped jobs. */
3544 do {
3545 jp1 = *jpp;
3546#if JOBS
3547 if (!jp1 || jp1->state != JOBSTOPPED)
3548#endif
3549 break;
3550 jpp = &jp1->prev_job;
3551 } while (1);
3552 /* FALLTHROUGH */
3553#if JOBS
3554 case CUR_STOPPED:
3555#endif
3556 /* newly stopped job - becomes curjob */
3557 jp->prev_job = *jpp;
3558 *jpp = jp;
3559 break;
3560 }
3561}
3562
3563#if JOBS || DEBUG
3564static int
3565jobno(const struct job *jp)
3566{
3567 return jp - jobtab + 1;
3568}
3569#endif
3570
3571/*
3572 * Convert a job name to a job structure.
3573 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003574#if !JOBS
3575#define getjob(name, getctl) getjob(name)
3576#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003577static struct job *
3578getjob(const char *name, int getctl)
3579{
3580 struct job *jp;
3581 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003582 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003583 unsigned num;
3584 int c;
3585 const char *p;
3586 char *(*match)(const char *, const char *);
3587
3588 jp = curjob;
3589 p = name;
3590 if (!p)
3591 goto currentjob;
3592
3593 if (*p != '%')
3594 goto err;
3595
3596 c = *++p;
3597 if (!c)
3598 goto currentjob;
3599
3600 if (!p[1]) {
3601 if (c == '+' || c == '%') {
3602 currentjob:
3603 err_msg = "No current job";
3604 goto check;
3605 }
3606 if (c == '-') {
3607 if (jp)
3608 jp = jp->prev_job;
3609 err_msg = "No previous job";
3610 check:
3611 if (!jp)
3612 goto err;
3613 goto gotit;
3614 }
3615 }
3616
3617 if (is_number(p)) {
3618 num = atoi(p);
3619 if (num < njobs) {
3620 jp = jobtab + num - 1;
3621 if (jp->used)
3622 goto gotit;
3623 goto err;
3624 }
3625 }
3626
3627 match = prefix;
3628 if (*p == '?') {
3629 match = strstr;
3630 p++;
3631 }
3632
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003633 found = NULL;
3634 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003635 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003636 if (found)
3637 goto err;
3638 found = jp;
3639 err_msg = "%s: ambiguous";
3640 }
3641 jp = jp->prev_job;
3642 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003643 if (!found)
3644 goto err;
3645 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003646
3647 gotit:
3648#if JOBS
3649 err_msg = "job %s not created under job control";
3650 if (getctl && jp->jobctl == 0)
3651 goto err;
3652#endif
3653 return jp;
3654 err:
3655 ash_msg_and_raise_error(err_msg, name);
3656}
3657
3658/*
3659 * Mark a job structure as unused.
3660 */
3661static void
3662freejob(struct job *jp)
3663{
3664 struct procstat *ps;
3665 int i;
3666
3667 INT_OFF;
3668 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003669 if (ps->ps_cmd != nullstr)
3670 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003671 }
3672 if (jp->ps != &jp->ps0)
3673 free(jp->ps);
3674 jp->used = 0;
3675 set_curjob(jp, CUR_DELETE);
3676 INT_ON;
3677}
3678
3679#if JOBS
3680static void
3681xtcsetpgrp(int fd, pid_t pgrp)
3682{
3683 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003684 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003685}
3686
3687/*
3688 * Turn job control on and off.
3689 *
3690 * Note: This code assumes that the third arg to ioctl is a character
3691 * pointer, which is true on Berkeley systems but not System V. Since
3692 * System V doesn't have job control yet, this isn't a problem now.
3693 *
3694 * Called with interrupts off.
3695 */
3696static void
3697setjobctl(int on)
3698{
3699 int fd;
3700 int pgrp;
3701
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003702 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003703 return;
3704 if (on) {
3705 int ofd;
3706 ofd = fd = open(_PATH_TTY, O_RDWR);
3707 if (fd < 0) {
3708 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3709 * That sometimes helps to acquire controlling tty.
3710 * Obviously, a workaround for bugs when someone
3711 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003712 fd = 2;
3713 while (!isatty(fd))
3714 if (--fd < 0)
3715 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003716 }
3717 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003718 if (ofd >= 0)
3719 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003720 if (fd < 0)
3721 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003722 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003723 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003724 do { /* while we are in the background */
3725 pgrp = tcgetpgrp(fd);
3726 if (pgrp < 0) {
3727 out:
3728 ash_msg("can't access tty; job control turned off");
3729 mflag = on = 0;
3730 goto close;
3731 }
3732 if (pgrp == getpgrp())
3733 break;
3734 killpg(0, SIGTTIN);
3735 } while (1);
3736 initialpgrp = pgrp;
3737
3738 setsignal(SIGTSTP);
3739 setsignal(SIGTTOU);
3740 setsignal(SIGTTIN);
3741 pgrp = rootpid;
3742 setpgid(0, pgrp);
3743 xtcsetpgrp(fd, pgrp);
3744 } else {
3745 /* turning job control off */
3746 fd = ttyfd;
3747 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003748 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003749 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003750 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003751 setpgid(0, pgrp);
3752 setsignal(SIGTSTP);
3753 setsignal(SIGTTOU);
3754 setsignal(SIGTTIN);
3755 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003756 if (fd >= 0)
3757 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003758 fd = -1;
3759 }
3760 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003761 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003762}
3763
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003764static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765killcmd(int argc, char **argv)
3766{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003767 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003768 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003769 do {
3770 if (argv[i][0] == '%') {
3771 struct job *jp = getjob(argv[i], 0);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003772 unsigned pid = jp->ps[0].ps_pid;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003773 /* Enough space for ' -NNN<nul>' */
3774 argv[i] = alloca(sizeof(int)*3 + 3);
3775 /* kill_main has matching code to expect
3776 * leading space. Needed to not confuse
3777 * negative pids with "kill -SIGNAL_NO" syntax */
3778 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003780 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003781 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003782 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003783}
3784
3785static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003786showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003787{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003788 struct procstat *ps;
3789 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790
Denys Vlasenko285ad152009-12-04 23:02:27 +01003791 psend = jp->ps + jp->nprocs;
3792 for (ps = jp->ps + 1; ps < psend; ps++)
3793 printf(" | %s", ps->ps_cmd);
3794 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795 flush_stdout_stderr();
3796}
3797
3798
3799static int
3800restartjob(struct job *jp, int mode)
3801{
3802 struct procstat *ps;
3803 int i;
3804 int status;
3805 pid_t pgid;
3806
3807 INT_OFF;
3808 if (jp->state == JOBDONE)
3809 goto out;
3810 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003811 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003812 if (mode == FORK_FG)
3813 xtcsetpgrp(ttyfd, pgid);
3814 killpg(pgid, SIGCONT);
3815 ps = jp->ps;
3816 i = jp->nprocs;
3817 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003818 if (WIFSTOPPED(ps->ps_status)) {
3819 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003820 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003821 ps++;
3822 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003823 out:
3824 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3825 INT_ON;
3826 return status;
3827}
3828
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003829static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003830fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003831{
3832 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003833 int mode;
3834 int retval;
3835
3836 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3837 nextopt(nullstr);
3838 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839 do {
3840 jp = getjob(*argv, 1);
3841 if (mode == FORK_BG) {
3842 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003843 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003844 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003845 out1str(jp->ps[0].ps_cmd);
3846 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003847 retval = restartjob(jp, mode);
3848 } while (*argv && *++argv);
3849 return retval;
3850}
3851#endif
3852
3853static int
3854sprint_status(char *s, int status, int sigonly)
3855{
3856 int col;
3857 int st;
3858
3859 col = 0;
3860 if (!WIFEXITED(status)) {
3861#if JOBS
3862 if (WIFSTOPPED(status))
3863 st = WSTOPSIG(status);
3864 else
3865#endif
3866 st = WTERMSIG(status);
3867 if (sigonly) {
3868 if (st == SIGINT || st == SIGPIPE)
3869 goto out;
3870#if JOBS
3871 if (WIFSTOPPED(status))
3872 goto out;
3873#endif
3874 }
3875 st &= 0x7f;
3876 col = fmtstr(s, 32, strsignal(st));
3877 if (WCOREDUMP(status)) {
3878 col += fmtstr(s + col, 16, " (core dumped)");
3879 }
3880 } else if (!sigonly) {
3881 st = WEXITSTATUS(status);
3882 if (st)
3883 col = fmtstr(s, 16, "Done(%d)", st);
3884 else
3885 col = fmtstr(s, 16, "Done");
3886 }
3887 out:
3888 return col;
3889}
3890
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003891static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003892dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003893{
3894 int pid;
3895 int status;
3896 struct job *jp;
3897 struct job *thisjob;
3898 int state;
3899
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003900 TRACE(("dowait(0x%x) called\n", wait_flags));
3901
3902 /* Do a wait system call. If job control is compiled in, we accept
3903 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3904 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003905 if (doing_jobctl)
3906 wait_flags |= WUNTRACED;
3907 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003908 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3909 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003910 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003911 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003912
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003913 INT_OFF;
3914 thisjob = NULL;
3915 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003916 struct procstat *ps;
3917 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003918 if (jp->state == JOBDONE)
3919 continue;
3920 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003921 ps = jp->ps;
3922 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003923 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003924 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003925 TRACE(("Job %d: changing status of proc %d "
3926 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003927 jobno(jp), pid, ps->ps_status, status));
3928 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003929 thisjob = jp;
3930 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003931 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932 state = JOBRUNNING;
3933#if JOBS
3934 if (state == JOBRUNNING)
3935 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003936 if (WIFSTOPPED(ps->ps_status)) {
3937 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003938 state = JOBSTOPPED;
3939 }
3940#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003941 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003942 if (thisjob)
3943 goto gotjob;
3944 }
3945#if JOBS
3946 if (!WIFSTOPPED(status))
3947#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003948 jobless--;
3949 goto out;
3950
3951 gotjob:
3952 if (state != JOBRUNNING) {
3953 thisjob->changed = 1;
3954
3955 if (thisjob->state != state) {
3956 TRACE(("Job %d: changing state from %d to %d\n",
3957 jobno(thisjob), thisjob->state, state));
3958 thisjob->state = state;
3959#if JOBS
3960 if (state == JOBSTOPPED) {
3961 set_curjob(thisjob, CUR_STOPPED);
3962 }
3963#endif
3964 }
3965 }
3966
3967 out:
3968 INT_ON;
3969
3970 if (thisjob && thisjob == job) {
3971 char s[48 + 1];
3972 int len;
3973
3974 len = sprint_status(s, status, 1);
3975 if (len) {
3976 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003977 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003978 out2str(s);
3979 }
3980 }
3981 return pid;
3982}
3983
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003984static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003985blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003986{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003987 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003988 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003989 raise_exception(EXSIG);
3990 return pid;
3991}
3992
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003993#if JOBS
3994static void
3995showjob(FILE *out, struct job *jp, int mode)
3996{
3997 struct procstat *ps;
3998 struct procstat *psend;
3999 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004000 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004001 char s[80];
4002
4003 ps = jp->ps;
4004
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004005 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004006 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004007 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004008 return;
4009 }
4010
4011 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004012 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004013
4014 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004015 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004016 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004017 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004018
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004019 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004020 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004021
4022 psend = ps + jp->nprocs;
4023
4024 if (jp->state == JOBRUNNING) {
4025 strcpy(s + col, "Running");
4026 col += sizeof("Running") - 1;
4027 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004028 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004029 if (jp->state == JOBSTOPPED)
4030 status = jp->stopstatus;
4031 col += sprint_status(s + col, status, 0);
4032 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004033 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004035 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4036 * or prints several "PID | <cmdN>" lines,
4037 * depending on SHOW_PIDS bit.
4038 * We do not print status of individual processes
4039 * between PID and <cmdN>. bash does it, but not very well:
4040 * first line shows overall job status, not process status,
4041 * making it impossible to know 1st process status.
4042 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004044 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004045 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004046 s[0] = '\0';
4047 col = 33;
4048 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004049 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004051 fprintf(out, "%s%*c%s%s",
4052 s,
4053 33 - col >= 0 ? 33 - col : 0, ' ',
4054 ps == jp->ps ? "" : "| ",
4055 ps->ps_cmd
4056 );
4057 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004058 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059
4060 jp->changed = 0;
4061
4062 if (jp->state == JOBDONE) {
4063 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4064 freejob(jp);
4065 }
4066}
4067
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068/*
4069 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4070 * statuses have changed since the last call to showjobs.
4071 */
4072static void
4073showjobs(FILE *out, int mode)
4074{
4075 struct job *jp;
4076
Denys Vlasenko883cea42009-07-11 15:31:59 +02004077 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004078
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004079 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004080 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 continue;
4082
4083 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004084 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004085 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004086 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004087 }
4088}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004089
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004090static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004091jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004092{
4093 int mode, m;
4094
4095 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004096 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004097 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004098 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004099 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004100 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004101 }
4102
4103 argv = argptr;
4104 if (*argv) {
4105 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004106 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004107 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004108 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004109 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004110 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004111
4112 return 0;
4113}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004114#endif /* JOBS */
4115
Michael Abbott359da5e2009-12-04 23:03:29 +01004116/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004117static int
4118getstatus(struct job *job)
4119{
4120 int status;
4121 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004122 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004123
Michael Abbott359da5e2009-12-04 23:03:29 +01004124 /* Fetch last member's status */
4125 ps = job->ps + job->nprocs - 1;
4126 status = ps->ps_status;
4127 if (pipefail) {
4128 /* "set -o pipefail" mode: use last _nonzero_ status */
4129 while (status == 0 && --ps >= job->ps)
4130 status = ps->ps_status;
4131 }
4132
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004133 retval = WEXITSTATUS(status);
4134 if (!WIFEXITED(status)) {
4135#if JOBS
4136 retval = WSTOPSIG(status);
4137 if (!WIFSTOPPED(status))
4138#endif
4139 {
4140 /* XXX: limits number of signals */
4141 retval = WTERMSIG(status);
4142#if JOBS
4143 if (retval == SIGINT)
4144 job->sigint = 1;
4145#endif
4146 }
4147 retval += 128;
4148 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004149 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004150 jobno(job), job->nprocs, status, retval));
4151 return retval;
4152}
4153
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004154static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004155waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004156{
4157 struct job *job;
4158 int retval;
4159 struct job *jp;
4160
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004161 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004162 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004163
4164 nextopt(nullstr);
4165 retval = 0;
4166
4167 argv = argptr;
4168 if (!*argv) {
4169 /* wait for all jobs */
4170 for (;;) {
4171 jp = curjob;
4172 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004173 if (!jp) /* no running procs */
4174 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175 if (jp->state == JOBRUNNING)
4176 break;
4177 jp->waited = 1;
4178 jp = jp->prev_job;
4179 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004180 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004181 /* man bash:
4182 * "When bash is waiting for an asynchronous command via
4183 * the wait builtin, the reception of a signal for which a trap
4184 * has been set will cause the wait builtin to return immediately
4185 * with an exit status greater than 128, immediately after which
4186 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004187 *
4188 * blocking_wait_with_raise_on_sig raises signal handlers
4189 * if it gets no pid (pid < 0). However,
4190 * if child sends us a signal *and immediately exits*,
4191 * blocking_wait_with_raise_on_sig gets pid > 0
4192 * and does not handle pending_sig. Check this case: */
4193 if (pending_sig)
4194 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004195 }
4196 }
4197
4198 retval = 127;
4199 do {
4200 if (**argv != '%') {
4201 pid_t pid = number(*argv);
4202 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004203 while (1) {
4204 if (!job)
4205 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004206 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004207 break;
4208 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004209 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004210 } else
4211 job = getjob(*argv, 0);
4212 /* loop until process terminated or stopped */
4213 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004214 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004215 job->waited = 1;
4216 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004217 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004218 } while (*++argv);
4219
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004220 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004221 return retval;
4222}
4223
4224static struct job *
4225growjobtab(void)
4226{
4227 size_t len;
4228 ptrdiff_t offset;
4229 struct job *jp, *jq;
4230
4231 len = njobs * sizeof(*jp);
4232 jq = jobtab;
4233 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4234
4235 offset = (char *)jp - (char *)jq;
4236 if (offset) {
4237 /* Relocate pointers */
4238 size_t l = len;
4239
4240 jq = (struct job *)((char *)jq + l);
4241 while (l) {
4242 l -= sizeof(*jp);
4243 jq--;
4244#define joff(p) ((struct job *)((char *)(p) + l))
4245#define jmove(p) (p) = (void *)((char *)(p) + offset)
4246 if (joff(jp)->ps == &jq->ps0)
4247 jmove(joff(jp)->ps);
4248 if (joff(jp)->prev_job)
4249 jmove(joff(jp)->prev_job);
4250 }
4251 if (curjob)
4252 jmove(curjob);
4253#undef joff
4254#undef jmove
4255 }
4256
4257 njobs += 4;
4258 jobtab = jp;
4259 jp = (struct job *)((char *)jp + len);
4260 jq = jp + 3;
4261 do {
4262 jq->used = 0;
4263 } while (--jq >= jp);
4264 return jp;
4265}
4266
4267/*
4268 * Return a new job structure.
4269 * Called with interrupts off.
4270 */
4271static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004272makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004273{
4274 int i;
4275 struct job *jp;
4276
4277 for (i = njobs, jp = jobtab; ; jp++) {
4278 if (--i < 0) {
4279 jp = growjobtab();
4280 break;
4281 }
4282 if (jp->used == 0)
4283 break;
4284 if (jp->state != JOBDONE || !jp->waited)
4285 continue;
4286#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004287 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004288 continue;
4289#endif
4290 freejob(jp);
4291 break;
4292 }
4293 memset(jp, 0, sizeof(*jp));
4294#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004295 /* jp->jobctl is a bitfield.
4296 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004297 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004298 jp->jobctl = 1;
4299#endif
4300 jp->prev_job = curjob;
4301 curjob = jp;
4302 jp->used = 1;
4303 jp->ps = &jp->ps0;
4304 if (nprocs > 1) {
4305 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4306 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004307 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004308 jobno(jp)));
4309 return jp;
4310}
4311
4312#if JOBS
4313/*
4314 * Return a string identifying a command (to be printed by the
4315 * jobs command).
4316 */
4317static char *cmdnextc;
4318
4319static void
4320cmdputs(const char *s)
4321{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004322 static const char vstype[VSTYPE + 1][3] = {
4323 "", "}", "-", "+", "?", "=",
4324 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004325 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004326 };
4327
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004328 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004329 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004330 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004331 unsigned char c;
4332 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004333 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004334
Denys Vlasenko46a14772009-12-10 21:27:13 +01004335 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004336 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4337 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004338 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004339 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004340 switch (c) {
4341 case CTLESC:
4342 c = *p++;
4343 break;
4344 case CTLVAR:
4345 subtype = *p++;
4346 if ((subtype & VSTYPE) == VSLENGTH)
4347 str = "${#";
4348 else
4349 str = "${";
4350 if (!(subtype & VSQUOTE) == !(quoted & 1))
4351 goto dostr;
4352 quoted ^= 1;
4353 c = '"';
4354 break;
4355 case CTLENDVAR:
4356 str = "\"}" + !(quoted & 1);
4357 quoted >>= 1;
4358 subtype = 0;
4359 goto dostr;
4360 case CTLBACKQ:
4361 str = "$(...)";
4362 goto dostr;
4363 case CTLBACKQ+CTLQUOTE:
4364 str = "\"$(...)\"";
4365 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004366#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004367 case CTLARI:
4368 str = "$((";
4369 goto dostr;
4370 case CTLENDARI:
4371 str = "))";
4372 goto dostr;
4373#endif
4374 case CTLQUOTEMARK:
4375 quoted ^= 1;
4376 c = '"';
4377 break;
4378 case '=':
4379 if (subtype == 0)
4380 break;
4381 if ((subtype & VSTYPE) != VSNORMAL)
4382 quoted <<= 1;
4383 str = vstype[subtype & VSTYPE];
4384 if (subtype & VSNUL)
4385 c = ':';
4386 else
4387 goto checkstr;
4388 break;
4389 case '\'':
4390 case '\\':
4391 case '"':
4392 case '$':
4393 /* These can only happen inside quotes */
4394 cc[0] = c;
4395 str = cc;
4396 c = '\\';
4397 break;
4398 default:
4399 break;
4400 }
4401 USTPUTC(c, nextc);
4402 checkstr:
4403 if (!str)
4404 continue;
4405 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004406 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004407 USTPUTC(c, nextc);
4408 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004409 } /* while *p++ not NUL */
4410
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004411 if (quoted & 1) {
4412 USTPUTC('"', nextc);
4413 }
4414 *nextc = 0;
4415 cmdnextc = nextc;
4416}
4417
4418/* cmdtxt() and cmdlist() call each other */
4419static void cmdtxt(union node *n);
4420
4421static void
4422cmdlist(union node *np, int sep)
4423{
4424 for (; np; np = np->narg.next) {
4425 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004426 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004427 cmdtxt(np);
4428 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004429 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004430 }
4431}
4432
4433static void
4434cmdtxt(union node *n)
4435{
4436 union node *np;
4437 struct nodelist *lp;
4438 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004439
4440 if (!n)
4441 return;
4442 switch (n->type) {
4443 default:
4444#if DEBUG
4445 abort();
4446#endif
4447 case NPIPE:
4448 lp = n->npipe.cmdlist;
4449 for (;;) {
4450 cmdtxt(lp->n);
4451 lp = lp->next;
4452 if (!lp)
4453 break;
4454 cmdputs(" | ");
4455 }
4456 break;
4457 case NSEMI:
4458 p = "; ";
4459 goto binop;
4460 case NAND:
4461 p = " && ";
4462 goto binop;
4463 case NOR:
4464 p = " || ";
4465 binop:
4466 cmdtxt(n->nbinary.ch1);
4467 cmdputs(p);
4468 n = n->nbinary.ch2;
4469 goto donode;
4470 case NREDIR:
4471 case NBACKGND:
4472 n = n->nredir.n;
4473 goto donode;
4474 case NNOT:
4475 cmdputs("!");
4476 n = n->nnot.com;
4477 donode:
4478 cmdtxt(n);
4479 break;
4480 case NIF:
4481 cmdputs("if ");
4482 cmdtxt(n->nif.test);
4483 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004484 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004485 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004486 cmdputs("; else ");
4487 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004488 } else {
4489 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004490 }
4491 p = "; fi";
4492 goto dotail;
4493 case NSUBSHELL:
4494 cmdputs("(");
4495 n = n->nredir.n;
4496 p = ")";
4497 goto dotail;
4498 case NWHILE:
4499 p = "while ";
4500 goto until;
4501 case NUNTIL:
4502 p = "until ";
4503 until:
4504 cmdputs(p);
4505 cmdtxt(n->nbinary.ch1);
4506 n = n->nbinary.ch2;
4507 p = "; done";
4508 dodo:
4509 cmdputs("; do ");
4510 dotail:
4511 cmdtxt(n);
4512 goto dotail2;
4513 case NFOR:
4514 cmdputs("for ");
4515 cmdputs(n->nfor.var);
4516 cmdputs(" in ");
4517 cmdlist(n->nfor.args, 1);
4518 n = n->nfor.body;
4519 p = "; done";
4520 goto dodo;
4521 case NDEFUN:
4522 cmdputs(n->narg.text);
4523 p = "() { ... }";
4524 goto dotail2;
4525 case NCMD:
4526 cmdlist(n->ncmd.args, 1);
4527 cmdlist(n->ncmd.redirect, 0);
4528 break;
4529 case NARG:
4530 p = n->narg.text;
4531 dotail2:
4532 cmdputs(p);
4533 break;
4534 case NHERE:
4535 case NXHERE:
4536 p = "<<...";
4537 goto dotail2;
4538 case NCASE:
4539 cmdputs("case ");
4540 cmdputs(n->ncase.expr->narg.text);
4541 cmdputs(" in ");
4542 for (np = n->ncase.cases; np; np = np->nclist.next) {
4543 cmdtxt(np->nclist.pattern);
4544 cmdputs(") ");
4545 cmdtxt(np->nclist.body);
4546 cmdputs(";; ");
4547 }
4548 p = "esac";
4549 goto dotail2;
4550 case NTO:
4551 p = ">";
4552 goto redir;
4553 case NCLOBBER:
4554 p = ">|";
4555 goto redir;
4556 case NAPPEND:
4557 p = ">>";
4558 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004559#if ENABLE_ASH_BASH_COMPAT
4560 case NTO2:
4561#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004562 case NTOFD:
4563 p = ">&";
4564 goto redir;
4565 case NFROM:
4566 p = "<";
4567 goto redir;
4568 case NFROMFD:
4569 p = "<&";
4570 goto redir;
4571 case NFROMTO:
4572 p = "<>";
4573 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004574 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004575 cmdputs(p);
4576 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004577 cmdputs(utoa(n->ndup.dupfd));
4578 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004579 }
4580 n = n->nfile.fname;
4581 goto donode;
4582 }
4583}
4584
4585static char *
4586commandtext(union node *n)
4587{
4588 char *name;
4589
4590 STARTSTACKSTR(cmdnextc);
4591 cmdtxt(n);
4592 name = stackblock();
4593 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4594 name, cmdnextc, cmdnextc));
4595 return ckstrdup(name);
4596}
4597#endif /* JOBS */
4598
4599/*
4600 * Fork off a subshell. If we are doing job control, give the subshell its
4601 * own process group. Jp is a job structure that the job is to be added to.
4602 * N is the command that will be evaluated by the child. Both jp and n may
4603 * be NULL. The mode parameter can be one of the following:
4604 * FORK_FG - Fork off a foreground process.
4605 * FORK_BG - Fork off a background process.
4606 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4607 * process group even if job control is on.
4608 *
4609 * When job control is turned off, background processes have their standard
4610 * input redirected to /dev/null (except for the second and later processes
4611 * in a pipeline).
4612 *
4613 * Called with interrupts off.
4614 */
4615/*
4616 * Clear traps on a fork.
4617 */
4618static void
4619clear_traps(void)
4620{
4621 char **tp;
4622
4623 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004624 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004625 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004626 if (trap_ptr == trap)
4627 free(*tp);
4628 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004629 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004630 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004631 setsignal(tp - trap);
4632 INT_ON;
4633 }
4634 }
4635}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004636
4637/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004638static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004639
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004640/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004641static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004642forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004643{
4644 int oldlvl;
4645
4646 TRACE(("Child shell %d\n", getpid()));
4647 oldlvl = shlvl;
4648 shlvl++;
4649
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004650 /* man bash: "Non-builtin commands run by bash have signal handlers
4651 * set to the values inherited by the shell from its parent".
4652 * Do we do it correctly? */
4653
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004654 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004655
4656 if (mode == FORK_NOJOB /* is it `xxx` ? */
4657 && n && n->type == NCMD /* is it single cmd? */
4658 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004659 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004660 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4661 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4662 ) {
4663 TRACE(("Trap hack\n"));
4664 /* Awful hack for `trap` or $(trap).
4665 *
4666 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4667 * contains an example where "trap" is executed in a subshell:
4668 *
4669 * save_traps=$(trap)
4670 * ...
4671 * eval "$save_traps"
4672 *
4673 * Standard does not say that "trap" in subshell shall print
4674 * parent shell's traps. It only says that its output
4675 * must have suitable form, but then, in the above example
4676 * (which is not supposed to be normative), it implies that.
4677 *
4678 * bash (and probably other shell) does implement it
4679 * (traps are reset to defaults, but "trap" still shows them),
4680 * but as a result, "trap" logic is hopelessly messed up:
4681 *
4682 * # trap
4683 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4684 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4685 * # true | trap <--- trap is in subshell - no output (ditto)
4686 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4687 * trap -- 'echo Ho' SIGWINCH
4688 * # echo `(trap)` <--- in subshell in subshell - output
4689 * trap -- 'echo Ho' SIGWINCH
4690 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4691 * trap -- 'echo Ho' SIGWINCH
4692 *
4693 * The rules when to forget and when to not forget traps
4694 * get really complex and nonsensical.
4695 *
4696 * Our solution: ONLY bare $(trap) or `trap` is special.
4697 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004698 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004699 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004700 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004701 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004702 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004703#if JOBS
4704 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004705 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004706 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4707 pid_t pgrp;
4708
4709 if (jp->nprocs == 0)
4710 pgrp = getpid();
4711 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004712 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004713 /* this can fail because we are doing it in the parent also */
4714 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004715 if (mode == FORK_FG)
4716 xtcsetpgrp(ttyfd, pgrp);
4717 setsignal(SIGTSTP);
4718 setsignal(SIGTTOU);
4719 } else
4720#endif
4721 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004722 /* man bash: "When job control is not in effect,
4723 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004724 ignoresig(SIGINT);
4725 ignoresig(SIGQUIT);
4726 if (jp->nprocs == 0) {
4727 close(0);
4728 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004729 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004730 }
4731 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004732 if (!oldlvl) {
4733 if (iflag) { /* why if iflag only? */
4734 setsignal(SIGINT);
4735 setsignal(SIGTERM);
4736 }
4737 /* man bash:
4738 * "In all cases, bash ignores SIGQUIT. Non-builtin
4739 * commands run by bash have signal handlers
4740 * set to the values inherited by the shell
4741 * from its parent".
4742 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004743 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004744 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004745#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004746 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004747 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004748 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004749 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004750 /* "jobs": we do not want to clear job list for it,
4751 * instead we remove only _its_ own_ job from job list.
4752 * This makes "jobs .... | cat" more useful.
4753 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004754 freejob(curjob);
4755 return;
4756 }
4757#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004758 for (jp = curjob; jp; jp = jp->prev_job)
4759 freejob(jp);
4760 jobless = 0;
4761}
4762
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004763/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004764#if !JOBS
4765#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4766#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004767static void
4768forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4769{
4770 TRACE(("In parent shell: child = %d\n", pid));
4771 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004772 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4773 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004774 jobless++;
4775 return;
4776 }
4777#if JOBS
4778 if (mode != FORK_NOJOB && jp->jobctl) {
4779 int pgrp;
4780
4781 if (jp->nprocs == 0)
4782 pgrp = pid;
4783 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004784 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004785 /* This can fail because we are doing it in the child also */
4786 setpgid(pid, pgrp);
4787 }
4788#endif
4789 if (mode == FORK_BG) {
4790 backgndpid = pid; /* set $! */
4791 set_curjob(jp, CUR_RUNNING);
4792 }
4793 if (jp) {
4794 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004795 ps->ps_pid = pid;
4796 ps->ps_status = -1;
4797 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004798#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004799 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004800 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004801#endif
4802 }
4803}
4804
4805static int
4806forkshell(struct job *jp, union node *n, int mode)
4807{
4808 int pid;
4809
4810 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4811 pid = fork();
4812 if (pid < 0) {
4813 TRACE(("Fork failed, errno=%d", errno));
4814 if (jp)
4815 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004816 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004817 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004818 if (pid == 0) {
4819 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004820 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004821 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004822 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004823 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004824 return pid;
4825}
4826
4827/*
4828 * Wait for job to finish.
4829 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004830 * Under job control we have the problem that while a child process
4831 * is running interrupts generated by the user are sent to the child
4832 * but not to the shell. This means that an infinite loop started by
4833 * an interactive user may be hard to kill. With job control turned off,
4834 * an interactive user may place an interactive program inside a loop.
4835 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004836 * these interrupts to also abort the loop. The approach we take here
4837 * is to have the shell ignore interrupt signals while waiting for a
4838 * foreground process to terminate, and then send itself an interrupt
4839 * signal if the child process was terminated by an interrupt signal.
4840 * Unfortunately, some programs want to do a bit of cleanup and then
4841 * exit on interrupt; unless these processes terminate themselves by
4842 * sending a signal to themselves (instead of calling exit) they will
4843 * confuse this approach.
4844 *
4845 * Called with interrupts off.
4846 */
4847static int
4848waitforjob(struct job *jp)
4849{
4850 int st;
4851
4852 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004853
4854 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004855 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004856 /* In non-interactive shells, we _can_ get
4857 * a keyboard signal here and be EINTRed,
4858 * but we just loop back, waiting for command to complete.
4859 *
4860 * man bash:
4861 * "If bash is waiting for a command to complete and receives
4862 * a signal for which a trap has been set, the trap
4863 * will not be executed until the command completes."
4864 *
4865 * Reality is that even if trap is not set, bash
4866 * will not act on the signal until command completes.
4867 * Try this. sleep5intoff.c:
4868 * #include <signal.h>
4869 * #include <unistd.h>
4870 * int main() {
4871 * sigset_t set;
4872 * sigemptyset(&set);
4873 * sigaddset(&set, SIGINT);
4874 * sigaddset(&set, SIGQUIT);
4875 * sigprocmask(SIG_BLOCK, &set, NULL);
4876 * sleep(5);
4877 * return 0;
4878 * }
4879 * $ bash -c './sleep5intoff; echo hi'
4880 * ^C^C^C^C <--- pressing ^C once a second
4881 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004882 * $ bash -c './sleep5intoff; echo hi'
4883 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4884 * $ _
4885 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004886 dowait(DOWAIT_BLOCK, jp);
4887 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004888 INT_ON;
4889
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004890 st = getstatus(jp);
4891#if JOBS
4892 if (jp->jobctl) {
4893 xtcsetpgrp(ttyfd, rootpid);
4894 /*
4895 * This is truly gross.
4896 * If we're doing job control, then we did a TIOCSPGRP which
4897 * caused us (the shell) to no longer be in the controlling
4898 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4899 * intuit from the subprocess exit status whether a SIGINT
4900 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4901 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004902 if (jp->sigint) /* TODO: do the same with all signals */
4903 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004904 }
4905 if (jp->state == JOBDONE)
4906#endif
4907 freejob(jp);
4908 return st;
4909}
4910
4911/*
4912 * return 1 if there are stopped jobs, otherwise 0
4913 */
4914static int
4915stoppedjobs(void)
4916{
4917 struct job *jp;
4918 int retval;
4919
4920 retval = 0;
4921 if (job_warning)
4922 goto out;
4923 jp = curjob;
4924 if (jp && jp->state == JOBSTOPPED) {
4925 out2str("You have stopped jobs.\n");
4926 job_warning = 2;
4927 retval++;
4928 }
4929 out:
4930 return retval;
4931}
4932
4933
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004934/* ============ redir.c
4935 *
4936 * Code for dealing with input/output redirection.
4937 */
4938
4939#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004940#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004941
4942/*
4943 * Open a file in noclobber mode.
4944 * The code was copied from bash.
4945 */
4946static int
4947noclobberopen(const char *fname)
4948{
4949 int r, fd;
4950 struct stat finfo, finfo2;
4951
4952 /*
4953 * If the file exists and is a regular file, return an error
4954 * immediately.
4955 */
4956 r = stat(fname, &finfo);
4957 if (r == 0 && S_ISREG(finfo.st_mode)) {
4958 errno = EEXIST;
4959 return -1;
4960 }
4961
4962 /*
4963 * If the file was not present (r != 0), make sure we open it
4964 * exclusively so that if it is created before we open it, our open
4965 * will fail. Make sure that we do not truncate an existing file.
4966 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4967 * file was not a regular file, we leave O_EXCL off.
4968 */
4969 if (r != 0)
4970 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4971 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4972
4973 /* If the open failed, return the file descriptor right away. */
4974 if (fd < 0)
4975 return fd;
4976
4977 /*
4978 * OK, the open succeeded, but the file may have been changed from a
4979 * non-regular file to a regular file between the stat and the open.
4980 * We are assuming that the O_EXCL open handles the case where FILENAME
4981 * did not exist and is symlinked to an existing file between the stat
4982 * and open.
4983 */
4984
4985 /*
4986 * If we can open it and fstat the file descriptor, and neither check
4987 * revealed that it was a regular file, and the file has not been
4988 * replaced, return the file descriptor.
4989 */
4990 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4991 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4992 return fd;
4993
4994 /* The file has been replaced. badness. */
4995 close(fd);
4996 errno = EEXIST;
4997 return -1;
4998}
4999
5000/*
5001 * Handle here documents. Normally we fork off a process to write the
5002 * data to a pipe. If the document is short, we can stuff the data in
5003 * the pipe without forking.
5004 */
5005/* openhere needs this forward reference */
5006static void expandhere(union node *arg, int fd);
5007static int
5008openhere(union node *redir)
5009{
5010 int pip[2];
5011 size_t len = 0;
5012
5013 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005014 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005015 if (redir->type == NHERE) {
5016 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005017 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005018 full_write(pip[1], redir->nhere.doc->narg.text, len);
5019 goto out;
5020 }
5021 }
5022 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005023 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005024 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005025 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5026 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5027 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5028 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005029 signal(SIGPIPE, SIG_DFL);
5030 if (redir->type == NHERE)
5031 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005032 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005033 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005034 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005035 }
5036 out:
5037 close(pip[1]);
5038 return pip[0];
5039}
5040
5041static int
5042openredirect(union node *redir)
5043{
5044 char *fname;
5045 int f;
5046
5047 switch (redir->nfile.type) {
5048 case NFROM:
5049 fname = redir->nfile.expfname;
5050 f = open(fname, O_RDONLY);
5051 if (f < 0)
5052 goto eopen;
5053 break;
5054 case NFROMTO:
5055 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005056 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005057 if (f < 0)
5058 goto ecreate;
5059 break;
5060 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005061#if ENABLE_ASH_BASH_COMPAT
5062 case NTO2:
5063#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005064 /* Take care of noclobber mode. */
5065 if (Cflag) {
5066 fname = redir->nfile.expfname;
5067 f = noclobberopen(fname);
5068 if (f < 0)
5069 goto ecreate;
5070 break;
5071 }
5072 /* FALLTHROUGH */
5073 case NCLOBBER:
5074 fname = redir->nfile.expfname;
5075 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5076 if (f < 0)
5077 goto ecreate;
5078 break;
5079 case NAPPEND:
5080 fname = redir->nfile.expfname;
5081 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5082 if (f < 0)
5083 goto ecreate;
5084 break;
5085 default:
5086#if DEBUG
5087 abort();
5088#endif
5089 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005090/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005091// case NTOFD:
5092// case NFROMFD:
5093// f = -1;
5094// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005095 case NHERE:
5096 case NXHERE:
5097 f = openhere(redir);
5098 break;
5099 }
5100
5101 return f;
5102 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005103 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005104 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005105 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005106}
5107
5108/*
5109 * Copy a file descriptor to be >= to. Returns -1
5110 * if the source file descriptor is closed, EMPTY if there are no unused
5111 * file descriptors left.
5112 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005113/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5114 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005115enum {
5116 COPYFD_EXACT = (int)~(INT_MAX),
5117 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5118};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005119static int
5120copyfd(int from, int to)
5121{
5122 int newfd;
5123
Denis Vlasenko5a867312008-07-24 19:46:38 +00005124 if (to & COPYFD_EXACT) {
5125 to &= ~COPYFD_EXACT;
5126 /*if (from != to)*/
5127 newfd = dup2(from, to);
5128 } else {
5129 newfd = fcntl(from, F_DUPFD, to);
5130 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005131 if (newfd < 0) {
5132 if (errno == EMFILE)
5133 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005134 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005135 ash_msg_and_raise_error("%d: %m", from);
5136 }
5137 return newfd;
5138}
5139
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005140/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005141struct two_fd_t {
5142 int orig, copy;
5143};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005144struct redirtab {
5145 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005146 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005147 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005148 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005149};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005150#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005151
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005152static int need_to_remember(struct redirtab *rp, int fd)
5153{
5154 int i;
5155
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005156 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005157 return 0;
5158
5159 for (i = 0; i < rp->pair_count; i++) {
5160 if (rp->two_fd[i].orig == fd) {
5161 /* already remembered */
5162 return 0;
5163 }
5164 }
5165 return 1;
5166}
5167
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005168/* "hidden" fd is a fd used to read scripts, or a copy of such */
5169static int is_hidden_fd(struct redirtab *rp, int fd)
5170{
5171 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005172 struct parsefile *pf;
5173
5174 if (fd == -1)
5175 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005176 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005177 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005178 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005179 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005180 * $ ash # running ash interactively
5181 * $ . ./script.sh
5182 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005183 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005184 * it's still ok to use it: "read" builtin uses it,
5185 * why should we cripple "exec" builtin?
5186 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005187 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005188 return 1;
5189 }
5190 pf = pf->prev;
5191 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005192
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005193 if (!rp)
5194 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005195 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005196 fd |= COPYFD_RESTORE;
5197 for (i = 0; i < rp->pair_count; i++) {
5198 if (rp->two_fd[i].copy == fd) {
5199 return 1;
5200 }
5201 }
5202 return 0;
5203}
5204
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005205/*
5206 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5207 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005208 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005209 */
5210/* flags passed to redirect */
5211#define REDIR_PUSH 01 /* save previous values of file descriptors */
5212#define REDIR_SAVEFD2 03 /* set preverrout */
5213static void
5214redirect(union node *redir, int flags)
5215{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005216 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005217 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005218 int i;
5219 int fd;
5220 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005221 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005222
Denis Vlasenko01631112007-12-16 17:20:38 +00005223 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005224 if (!redir) {
5225 return;
5226 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005227
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005228 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005229 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005230 INT_OFF;
5231 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005232 union node *tmp = redir;
5233 do {
5234 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005235#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005236 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005237 sv_pos++;
5238#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005239 tmp = tmp->nfile.next;
5240 } while (tmp);
5241 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005242 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005243 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005244 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005245 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005246 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005247 while (sv_pos > 0) {
5248 sv_pos--;
5249 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5250 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005251 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005252
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005253 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005254 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005255 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005256 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005257 right_fd = redir->ndup.dupfd;
5258 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005259 /* redirect from/to same file descriptor? */
5260 if (right_fd == fd)
5261 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005262 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005263 if (is_hidden_fd(sv, right_fd)) {
5264 errno = EBADF; /* as if it is closed */
5265 ash_msg_and_raise_error("%d: %m", right_fd);
5266 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005267 newfd = -1;
5268 } else {
5269 newfd = openredirect(redir); /* always >= 0 */
5270 if (fd == newfd) {
5271 /* Descriptor wasn't open before redirect.
5272 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005273 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005274 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005275 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005276 continue;
5277 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005278 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005279#if ENABLE_ASH_BASH_COMPAT
5280 redirect_more:
5281#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005282 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005283 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005284 /* Careful to not accidentally "save"
5285 * to the same fd as right side fd in N>&M */
5286 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5287 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005288/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5289 * are closed in popredir() in the child, preventing them from leaking
5290 * into child. (popredir() also cleans up the mess in case of failures)
5291 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005292 if (i == -1) {
5293 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005294 if (i != EBADF) {
5295 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005296 if (newfd >= 0)
5297 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005298 errno = i;
5299 ash_msg_and_raise_error("%d: %m", fd);
5300 /* NOTREACHED */
5301 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005302 /* EBADF: it is not open - good, remember to close it */
5303 remember_to_close:
5304 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005305 } else { /* fd is open, save its copy */
5306 /* "exec fd>&-" should not close fds
5307 * which point to script file(s).
5308 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005309 if (is_hidden_fd(sv, fd))
5310 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005311 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005312 if (fd == 2)
5313 copied_fd2 = i;
5314 sv->two_fd[sv_pos].orig = fd;
5315 sv->two_fd[sv_pos].copy = i;
5316 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005317 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005318 if (newfd < 0) {
5319 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005320 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005321 /* Don't want to trigger debugging */
5322 if (fd != -1)
5323 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005324 } else {
5325 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005326 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005327 } else if (fd != newfd) { /* move newfd to fd */
5328 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005329#if ENABLE_ASH_BASH_COMPAT
5330 if (!(redir->nfile.type == NTO2 && fd == 2))
5331#endif
5332 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005333 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005334#if ENABLE_ASH_BASH_COMPAT
5335 if (redir->nfile.type == NTO2 && fd == 1) {
5336 /* We already redirected it to fd 1, now copy it to 2 */
5337 newfd = 1;
5338 fd = 2;
5339 goto redirect_more;
5340 }
5341#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005342 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005343
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005344 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005345 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5346 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005347}
5348
5349/*
5350 * Undo the effects of the last redirection.
5351 */
5352static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005353popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005354{
5355 struct redirtab *rp;
5356 int i;
5357
Denis Vlasenko01631112007-12-16 17:20:38 +00005358 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005359 return;
5360 INT_OFF;
5361 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005362 for (i = 0; i < rp->pair_count; i++) {
5363 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005364 int copy = rp->two_fd[i].copy;
5365 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005366 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005367 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005368 continue;
5369 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005370 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005371 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005372 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005373 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005374 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005375 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005376 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005377 }
5378 }
5379 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005380 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005381 free(rp);
5382 INT_ON;
5383}
5384
5385/*
5386 * Undo all redirections. Called on error or interrupt.
5387 */
5388
5389/*
5390 * Discard all saved file descriptors.
5391 */
5392static void
5393clearredir(int drop)
5394{
5395 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005396 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005397 if (!redirlist)
5398 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005399 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005400 }
5401}
5402
5403static int
5404redirectsafe(union node *redir, int flags)
5405{
5406 int err;
5407 volatile int saveint;
5408 struct jmploc *volatile savehandler = exception_handler;
5409 struct jmploc jmploc;
5410
5411 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005412 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5413 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005414 if (!err) {
5415 exception_handler = &jmploc;
5416 redirect(redir, flags);
5417 }
5418 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005419 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005420 longjmp(exception_handler->loc, 1);
5421 RESTORE_INT(saveint);
5422 return err;
5423}
5424
5425
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005426/* ============ Routines to expand arguments to commands
5427 *
5428 * We have to deal with backquotes, shell variables, and file metacharacters.
5429 */
5430
Mike Frysinger98c52642009-04-02 10:02:37 +00005431#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005432static arith_t
5433ash_arith(const char *s)
5434{
5435 arith_eval_hooks_t math_hooks;
5436 arith_t result;
5437 int errcode = 0;
5438
5439 math_hooks.lookupvar = lookupvar;
Denys Vlasenko03dad222010-01-12 23:29:57 +01005440 math_hooks.setvar = setvar2;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005441 math_hooks.endofname = endofname;
5442
5443 INT_OFF;
5444 result = arith(s, &errcode, &math_hooks);
5445 if (errcode < 0) {
5446 if (errcode == -3)
5447 ash_msg_and_raise_error("exponent less than 0");
5448 if (errcode == -2)
5449 ash_msg_and_raise_error("divide by zero");
5450 if (errcode == -5)
5451 ash_msg_and_raise_error("expression recursion loop detected");
5452 raise_error_syntax(s);
5453 }
5454 INT_ON;
5455
5456 return result;
5457}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005458#endif
5459
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005460/*
5461 * expandarg flags
5462 */
5463#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5464#define EXP_TILDE 0x2 /* do normal tilde expansion */
5465#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5466#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5467#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5468#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5469#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5470#define EXP_WORD 0x80 /* expand word in parameter expansion */
5471#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5472/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005473 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005474 */
5475#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5476#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5477#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5478#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5479#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5480
5481/*
5482 * Structure specifying which parts of the string should be searched
5483 * for IFS characters.
5484 */
5485struct ifsregion {
5486 struct ifsregion *next; /* next region in list */
5487 int begoff; /* offset of start of region */
5488 int endoff; /* offset of end of region */
5489 int nulonly; /* search for nul bytes only */
5490};
5491
5492struct arglist {
5493 struct strlist *list;
5494 struct strlist **lastp;
5495};
5496
5497/* output of current string */
5498static char *expdest;
5499/* list of back quote expressions */
5500static struct nodelist *argbackq;
5501/* first struct in list of ifs regions */
5502static struct ifsregion ifsfirst;
5503/* last struct in list */
5504static struct ifsregion *ifslastp;
5505/* holds expanded arg list */
5506static struct arglist exparg;
5507
5508/*
5509 * Our own itoa().
5510 */
5511static int
5512cvtnum(arith_t num)
5513{
5514 int len;
5515
5516 expdest = makestrspace(32, expdest);
Mike Frysinger98c52642009-04-02 10:02:37 +00005517 len = fmtstr(expdest, 32, arith_t_fmt, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005518 STADJUST(len, expdest);
5519 return len;
5520}
5521
5522static size_t
5523esclen(const char *start, const char *p)
5524{
5525 size_t esc = 0;
5526
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005527 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005528 esc++;
5529 }
5530 return esc;
5531}
5532
5533/*
5534 * Remove any CTLESC characters from a string.
5535 */
5536static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005537rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005538{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005539 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005540
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005541 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005542 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005543 unsigned protect_against_glob;
5544 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005545
5546 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005547 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005548 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005549
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005550 q = p;
5551 r = str;
5552 if (flag & RMESCAPE_ALLOC) {
5553 size_t len = p - str;
5554 size_t fulllen = len + strlen(p) + 1;
5555
5556 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005557 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005558 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005559 /* p and str may be invalidated by makestrspace */
5560 str = (char *)stackblock() + strloc;
5561 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005562 } else if (flag & RMESCAPE_HEAP) {
5563 r = ckmalloc(fulllen);
5564 } else {
5565 r = stalloc(fulllen);
5566 }
5567 q = r;
5568 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005569 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005570 }
5571 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005572
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005573 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5574 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005575 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005576 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005577 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005578// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5579// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5580// Note: both inquotes and protect_against_glob only affect whether
5581// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005582 inquotes = ~inquotes;
5583 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005584 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005585 continue;
5586 }
5587 if (*p == '\\') {
5588 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005589 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005590 goto copy;
5591 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005592 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005593 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005594 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005595 *q++ = '\\';
5596 }
5597 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005598 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005599 copy:
5600 *q++ = *p++;
5601 }
5602 *q = '\0';
5603 if (flag & RMESCAPE_GROW) {
5604 expdest = r;
5605 STADJUST(q - r + 1, expdest);
5606 }
5607 return r;
5608}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005609#define pmatch(a, b) !fnmatch((a), (b), 0)
5610
5611/*
5612 * Prepare a pattern for a expmeta (internal glob(3)) call.
5613 *
5614 * Returns an stalloced string.
5615 */
5616static char *
5617preglob(const char *pattern, int quoted, int flag)
5618{
5619 flag |= RMESCAPE_GLOB;
5620 if (quoted) {
5621 flag |= RMESCAPE_QUOTED;
5622 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005623 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005624}
5625
5626/*
5627 * Put a string on the stack.
5628 */
5629static void
5630memtodest(const char *p, size_t len, int syntax, int quotes)
5631{
5632 char *q = expdest;
5633
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005634 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005635
5636 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005637 unsigned char c = *p++;
5638 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005639 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005640 if (quotes) {
5641 int n = SIT(c, syntax);
5642 if (n == CCTL || n == CBACK)
5643 USTPUTC(CTLESC, q);
5644 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005645 USTPUTC(c, q);
5646 }
5647
5648 expdest = q;
5649}
5650
5651static void
5652strtodest(const char *p, int syntax, int quotes)
5653{
5654 memtodest(p, strlen(p), syntax, quotes);
5655}
5656
5657/*
5658 * Record the fact that we have to scan this region of the
5659 * string for IFS characters.
5660 */
5661static void
5662recordregion(int start, int end, int nulonly)
5663{
5664 struct ifsregion *ifsp;
5665
5666 if (ifslastp == NULL) {
5667 ifsp = &ifsfirst;
5668 } else {
5669 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005670 ifsp = ckzalloc(sizeof(*ifsp));
5671 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005672 ifslastp->next = ifsp;
5673 INT_ON;
5674 }
5675 ifslastp = ifsp;
5676 ifslastp->begoff = start;
5677 ifslastp->endoff = end;
5678 ifslastp->nulonly = nulonly;
5679}
5680
5681static void
5682removerecordregions(int endoff)
5683{
5684 if (ifslastp == NULL)
5685 return;
5686
5687 if (ifsfirst.endoff > endoff) {
5688 while (ifsfirst.next != NULL) {
5689 struct ifsregion *ifsp;
5690 INT_OFF;
5691 ifsp = ifsfirst.next->next;
5692 free(ifsfirst.next);
5693 ifsfirst.next = ifsp;
5694 INT_ON;
5695 }
5696 if (ifsfirst.begoff > endoff)
5697 ifslastp = NULL;
5698 else {
5699 ifslastp = &ifsfirst;
5700 ifsfirst.endoff = endoff;
5701 }
5702 return;
5703 }
5704
5705 ifslastp = &ifsfirst;
5706 while (ifslastp->next && ifslastp->next->begoff < endoff)
5707 ifslastp=ifslastp->next;
5708 while (ifslastp->next != NULL) {
5709 struct ifsregion *ifsp;
5710 INT_OFF;
5711 ifsp = ifslastp->next->next;
5712 free(ifslastp->next);
5713 ifslastp->next = ifsp;
5714 INT_ON;
5715 }
5716 if (ifslastp->endoff > endoff)
5717 ifslastp->endoff = endoff;
5718}
5719
5720static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005721exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005722{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005723 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005724 char *name;
5725 struct passwd *pw;
5726 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005727 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005728 int startloc;
5729
5730 name = p + 1;
5731
5732 while ((c = *++p) != '\0') {
5733 switch (c) {
5734 case CTLESC:
5735 return startp;
5736 case CTLQUOTEMARK:
5737 return startp;
5738 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005739 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005740 goto done;
5741 break;
5742 case '/':
5743 case CTLENDVAR:
5744 goto done;
5745 }
5746 }
5747 done:
5748 *p = '\0';
5749 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005750 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005751 } else {
5752 pw = getpwnam(name);
5753 if (pw == NULL)
5754 goto lose;
5755 home = pw->pw_dir;
5756 }
5757 if (!home || !*home)
5758 goto lose;
5759 *p = c;
5760 startloc = expdest - (char *)stackblock();
5761 strtodest(home, SQSYNTAX, quotes);
5762 recordregion(startloc, expdest - (char *)stackblock(), 0);
5763 return p;
5764 lose:
5765 *p = c;
5766 return startp;
5767}
5768
5769/*
5770 * Execute a command inside back quotes. If it's a builtin command, we
5771 * want to save its output in a block obtained from malloc. Otherwise
5772 * we fork off a subprocess and get the output of the command via a pipe.
5773 * Should be called with interrupts off.
5774 */
5775struct backcmd { /* result of evalbackcmd */
5776 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005777 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005778 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005779 struct job *jp; /* job structure for command */
5780};
5781
5782/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005783static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005784#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005785static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005786
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005787static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005788evalbackcmd(union node *n, struct backcmd *result)
5789{
5790 int saveherefd;
5791
5792 result->fd = -1;
5793 result->buf = NULL;
5794 result->nleft = 0;
5795 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005796 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005797 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005798
5799 saveherefd = herefd;
5800 herefd = -1;
5801
5802 {
5803 int pip[2];
5804 struct job *jp;
5805
5806 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005807 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005808 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005809 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5810 FORCE_INT_ON;
5811 close(pip[0]);
5812 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005813 /*close(1);*/
5814 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005815 close(pip[1]);
5816 }
5817 eflag = 0;
5818 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5819 /* NOTREACHED */
5820 }
5821 close(pip[1]);
5822 result->fd = pip[0];
5823 result->jp = jp;
5824 }
5825 herefd = saveherefd;
5826 out:
5827 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5828 result->fd, result->buf, result->nleft, result->jp));
5829}
5830
5831/*
5832 * Expand stuff in backwards quotes.
5833 */
5834static void
5835expbackq(union node *cmd, int quoted, int quotes)
5836{
5837 struct backcmd in;
5838 int i;
5839 char buf[128];
5840 char *p;
5841 char *dest;
5842 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005843 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005844 struct stackmark smark;
5845
5846 INT_OFF;
5847 setstackmark(&smark);
5848 dest = expdest;
5849 startloc = dest - (char *)stackblock();
5850 grabstackstr(dest);
5851 evalbackcmd(cmd, &in);
5852 popstackmark(&smark);
5853
5854 p = in.buf;
5855 i = in.nleft;
5856 if (i == 0)
5857 goto read;
5858 for (;;) {
5859 memtodest(p, i, syntax, quotes);
5860 read:
5861 if (in.fd < 0)
5862 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005863 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005864 TRACE(("expbackq: read returns %d\n", i));
5865 if (i <= 0)
5866 break;
5867 p = buf;
5868 }
5869
Denis Vlasenko60818682007-09-28 22:07:23 +00005870 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005871 if (in.fd >= 0) {
5872 close(in.fd);
5873 back_exitstatus = waitforjob(in.jp);
5874 }
5875 INT_ON;
5876
5877 /* Eat all trailing newlines */
5878 dest = expdest;
5879 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5880 STUNPUTC(dest);
5881 expdest = dest;
5882
5883 if (quoted == 0)
5884 recordregion(startloc, dest - (char *)stackblock(), 0);
5885 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5886 (dest - (char *)stackblock()) - startloc,
5887 (dest - (char *)stackblock()) - startloc,
5888 stackblock() + startloc));
5889}
5890
Mike Frysinger98c52642009-04-02 10:02:37 +00005891#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005892/*
5893 * Expand arithmetic expression. Backup to start of expression,
5894 * evaluate, place result in (backed up) result, adjust string position.
5895 */
5896static void
5897expari(int quotes)
5898{
5899 char *p, *start;
5900 int begoff;
5901 int flag;
5902 int len;
5903
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005904 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005905
5906 /*
5907 * This routine is slightly over-complicated for
5908 * efficiency. Next we scan backwards looking for the
5909 * start of arithmetic.
5910 */
5911 start = stackblock();
5912 p = expdest - 1;
5913 *p = '\0';
5914 p--;
5915 do {
5916 int esc;
5917
Denys Vlasenkocd716832009-11-28 22:14:02 +01005918 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005919 p--;
5920#if DEBUG
5921 if (p < start) {
5922 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5923 }
5924#endif
5925 }
5926
5927 esc = esclen(start, p);
5928 if (!(esc % 2)) {
5929 break;
5930 }
5931
5932 p -= esc + 1;
5933 } while (1);
5934
5935 begoff = p - start;
5936
5937 removerecordregions(begoff);
5938
5939 flag = p[1];
5940
5941 expdest = p;
5942
5943 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005944 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005945
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005946 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005947
5948 if (flag != '"')
5949 recordregion(begoff, begoff + len, 0);
5950}
5951#endif
5952
5953/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005954static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005955
5956/*
5957 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5958 * characters to allow for further processing. Otherwise treat
5959 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005960 *
5961 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5962 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5963 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005964 */
5965static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005966argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005967{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005968 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005969 '=',
5970 ':',
5971 CTLQUOTEMARK,
5972 CTLENDVAR,
5973 CTLESC,
5974 CTLVAR,
5975 CTLBACKQ,
5976 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00005977#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005978 CTLENDARI,
5979#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01005980 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005981 };
5982 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005983 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
5984 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005985 int inquotes;
5986 size_t length;
5987 int startloc;
5988
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005989 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005990 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005991 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005992 reject++;
5993 }
5994 inquotes = 0;
5995 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005996 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005997 char *q;
5998
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005999 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006000 tilde:
6001 q = p;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006002 if (*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006003 q++;
6004 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006005 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006006 }
6007 start:
6008 startloc = expdest - (char *)stackblock();
6009 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006010 unsigned char c;
6011
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006012 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006013 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006014 if (c) {
6015 if (!(c & 0x80)
Mike Frysinger98c52642009-04-02 10:02:37 +00006016#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006017 || c == CTLENDARI
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006018#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006019 ) {
6020 /* c == '=' || c == ':' || c == CTLENDARI */
6021 length++;
6022 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023 }
6024 if (length > 0) {
6025 int newloc;
6026 expdest = stack_nputstr(p, length, expdest);
6027 newloc = expdest - (char *)stackblock();
6028 if (breakall && !inquotes && newloc > startloc) {
6029 recordregion(startloc, newloc, 0);
6030 }
6031 startloc = newloc;
6032 }
6033 p += length + 1;
6034 length = 0;
6035
6036 switch (c) {
6037 case '\0':
6038 goto breakloop;
6039 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006040 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006041 p--;
6042 continue;
6043 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006044 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045 reject++;
6046 /* fall through */
6047 case ':':
6048 /*
6049 * sort of a hack - expand tildes in variable
6050 * assignments (after the first '=' and after ':'s).
6051 */
6052 if (*--p == '~') {
6053 goto tilde;
6054 }
6055 continue;
6056 }
6057
6058 switch (c) {
6059 case CTLENDVAR: /* ??? */
6060 goto breakloop;
6061 case CTLQUOTEMARK:
6062 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006063 if (!inquotes
6064 && memcmp(p, dolatstr, 4) == 0
6065 && ( p[4] == CTLQUOTEMARK
6066 || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK)
6067 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006068 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006069 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006070 goto start;
6071 }
6072 inquotes = !inquotes;
6073 addquote:
6074 if (quotes) {
6075 p--;
6076 length++;
6077 startloc++;
6078 }
6079 break;
6080 case CTLESC:
6081 startloc++;
6082 length++;
6083 goto addquote;
6084 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006085 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006086 goto start;
6087 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006088 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006089 case CTLBACKQ|CTLQUOTE:
6090 expbackq(argbackq->n, c, quotes);
6091 argbackq = argbackq->next;
6092 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006093#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006094 case CTLENDARI:
6095 p--;
6096 expari(quotes);
6097 goto start;
6098#endif
6099 }
6100 }
6101 breakloop:
6102 ;
6103}
6104
6105static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006106scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006107 int zero)
6108{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006109// This commented out code was added by James Simmons <jsimmons@infradead.org>
6110// as part of a larger change when he added support for ${var/a/b}.
6111// However, it broke # and % operators:
6112//
6113//var=ababcdcd
6114// ok bad
6115//echo ${var#ab} abcdcd abcdcd
6116//echo ${var##ab} abcdcd abcdcd
6117//echo ${var#a*b} abcdcd ababcdcd (!)
6118//echo ${var##a*b} cdcd cdcd
6119//echo ${var#?} babcdcd ababcdcd (!)
6120//echo ${var##?} babcdcd babcdcd
6121//echo ${var#*} ababcdcd babcdcd (!)
6122//echo ${var##*}
6123//echo ${var%cd} ababcd ababcd
6124//echo ${var%%cd} ababcd abab (!)
6125//echo ${var%c*d} ababcd ababcd
6126//echo ${var%%c*d} abab ababcdcd (!)
6127//echo ${var%?} ababcdc ababcdc
6128//echo ${var%%?} ababcdc ababcdcd (!)
6129//echo ${var%*} ababcdcd ababcdcd
6130//echo ${var%%*}
6131//
6132// Commenting it back out helped. Remove it completely if it really
6133// is not needed.
6134
6135 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006136 char c;
6137
6138 loc = startp;
6139 loc2 = rmesc;
6140 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006141 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006142 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006143
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006144 c = *loc2;
6145 if (zero) {
6146 *loc2 = '\0';
6147 s = rmesc;
6148 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006149 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006150
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006151// // chop off end if its '*'
6152// full = strrchr(str, '*');
6153// if (full && full != str)
6154// match--;
6155//
6156// // If str starts with '*' replace with s.
6157// if ((*str == '*') && strlen(s) >= match) {
6158// full = xstrdup(s);
6159// strncpy(full+strlen(s)-match+1, str+1, match-1);
6160// } else
6161// full = xstrndup(str, match);
6162// match = strncmp(s, full, strlen(full));
6163// free(full);
6164//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006165 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006166 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006167 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006168 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006169 loc++;
6170 loc++;
6171 loc2++;
6172 } while (c);
6173 return 0;
6174}
6175
6176static char *
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006177scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006178{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006179#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6180 int try2optimize = match_at_start;
6181#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182 int esc = 0;
6183 char *loc;
6184 char *loc2;
6185
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006186 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6187 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6188 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6189 * Logic:
6190 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6191 * and on each iteration they go back two/one char until they reach the beginning.
6192 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6193 */
6194 /* TODO: document in what other circumstances we are called. */
6195
6196 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006197 int match;
6198 char c = *loc2;
6199 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006200 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006201 *loc2 = '\0';
6202 s = rmesc;
6203 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006204 match = pmatch(pattern, s);
6205 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006206 *loc2 = c;
6207 if (match)
6208 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006209#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6210 if (try2optimize) {
6211 /* Maybe we can optimize this:
6212 * if pattern ends with unescaped *, we can avoid checking
6213 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6214 * it wont match truncated "raw_value_of_" strings too.
6215 */
6216 unsigned plen = strlen(pattern);
6217 /* Does it end with "*"? */
6218 if (plen != 0 && pattern[--plen] == '*') {
6219 /* "xxxx*" is not escaped */
6220 /* "xxx\*" is escaped */
6221 /* "xx\\*" is not escaped */
6222 /* "x\\\*" is escaped */
6223 int slashes = 0;
6224 while (plen != 0 && pattern[--plen] == '\\')
6225 slashes++;
6226 if (!(slashes & 1))
6227 break; /* ends with unescaped "*" */
6228 }
6229 try2optimize = 0;
6230 }
6231#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006232 loc--;
6233 if (quotes) {
6234 if (--esc < 0) {
6235 esc = esclen(startp, loc);
6236 }
6237 if (esc % 2) {
6238 esc--;
6239 loc--;
6240 }
6241 }
6242 }
6243 return 0;
6244}
6245
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006246static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006247static void
6248varunset(const char *end, const char *var, const char *umsg, int varflags)
6249{
6250 const char *msg;
6251 const char *tail;
6252
6253 tail = nullstr;
6254 msg = "parameter not set";
6255 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006256 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006257 if (varflags & VSNUL)
6258 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006259 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006260 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006261 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006262 }
6263 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
6264}
6265
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006266#if ENABLE_ASH_BASH_COMPAT
6267static char *
6268parse_sub_pattern(char *arg, int inquotes)
6269{
6270 char *idx, *repl = NULL;
6271 unsigned char c;
6272
Denis Vlasenko2659c632008-06-14 06:04:59 +00006273 idx = arg;
6274 while (1) {
6275 c = *arg;
6276 if (!c)
6277 break;
6278 if (c == '/') {
6279 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006280 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006281 repl = idx + 1;
6282 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006283 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006284 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006285 *idx++ = c;
6286 if (!inquotes && c == '\\' && arg[1] == '\\')
6287 arg++; /* skip both \\, not just first one */
6288 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006289 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006290 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006291
6292 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006293}
6294#endif /* ENABLE_ASH_BASH_COMPAT */
6295
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006297subevalvar(char *p, char *str, int strloc, int subtype,
6298 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006299{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006300 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006301 char *startp;
6302 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006303 char *rmesc, *rmescend;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006304 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006305 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006306 int saveherefd = herefd;
6307 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006308 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006309 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006310
6311 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006312 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6313 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006314 STPUTC('\0', expdest);
6315 herefd = saveherefd;
6316 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006317 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006318
6319 switch (subtype) {
6320 case VSASSIGN:
6321 setvar(str, startp, 0);
6322 amount = startp - expdest;
6323 STADJUST(amount, expdest);
6324 return startp;
6325
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006326#if ENABLE_ASH_BASH_COMPAT
6327 case VSSUBSTR:
6328 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006329 /* Read POS in ${var:POS:LEN} */
6330 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006331 len = str - startp - 1;
6332
6333 /* *loc != '\0', guaranteed by parser */
6334 if (quotes) {
6335 char *ptr;
6336
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006337 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006338 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006339 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006340 len--;
6341 ptr++;
6342 }
6343 }
6344 }
6345 orig_len = len;
6346
6347 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006348 /* ${var::LEN} */
6349 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006350 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006351 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006352 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006353 while (*loc && *loc != ':') {
6354 /* TODO?
6355 * bash complains on: var=qwe; echo ${var:1a:123}
6356 if (!isdigit(*loc))
6357 ash_msg_and_raise_error(msg_illnum, str);
6358 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006359 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006360 }
6361 if (*loc++ == ':') {
6362 len = number(loc);
6363 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006364 }
6365 if (pos >= orig_len) {
6366 pos = 0;
6367 len = 0;
6368 }
6369 if (len > (orig_len - pos))
6370 len = orig_len - pos;
6371
6372 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006373 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006374 str++;
6375 }
6376 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006377 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006378 *loc++ = *str++;
6379 *loc++ = *str++;
6380 }
6381 *loc = '\0';
6382 amount = loc - expdest;
6383 STADJUST(amount, expdest);
6384 return loc;
6385#endif
6386
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006387 case VSQUESTION:
6388 varunset(p, str, startp, varflags);
6389 /* NOTREACHED */
6390 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006391 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006392
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006393 /* We'll comeback here if we grow the stack while handling
6394 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6395 * stack will need rebasing, and we'll need to remove our work
6396 * areas each time
6397 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006398 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006399
6400 amount = expdest - ((char *)stackblock() + resetloc);
6401 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006402 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006403
6404 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006405 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006406 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006407 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006408 if (rmesc != startp) {
6409 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006410 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006411 }
6412 }
6413 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006414 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006415 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006416 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006417
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006418#if ENABLE_ASH_BASH_COMPAT
6419 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006420 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006421
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006422 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006423 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6424 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006425 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006426 }
6427
6428 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006429 if (str[0] == '\0')
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006430 return 0;
6431
6432 len = 0;
6433 idx = startp;
6434 end = str - 1;
6435 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006436 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006437 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6438 if (!loc) {
6439 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006440 char *restart_detect = stackblock();
6441 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006442 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006443 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006444 idx++;
6445 len++;
6446 STPUTC(*idx, expdest);
6447 }
6448 if (stackblock() != restart_detect)
6449 goto restart;
6450 idx++;
6451 len++;
6452 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006453 /* continue; - prone to quadratic behavior, smarter code: */
6454 if (idx >= end)
6455 break;
6456 if (str[0] == '*') {
6457 /* Pattern is "*foo". If "*foo" does not match "long_string",
6458 * it would never match "ong_string" etc, no point in trying.
6459 */
6460 goto skip_matching;
6461 }
6462 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006463 }
6464
6465 if (subtype == VSREPLACEALL) {
6466 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006467 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006468 idx++;
6469 idx++;
6470 rmesc++;
6471 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006472 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006473 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006474 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006475
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006476 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006477 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006478 if (quotes && *loc == '\\') {
6479 STPUTC(CTLESC, expdest);
6480 len++;
6481 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006482 STPUTC(*loc, expdest);
6483 if (stackblock() != restart_detect)
6484 goto restart;
6485 len++;
6486 }
6487
6488 if (subtype == VSREPLACE) {
6489 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006490 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006491 if (quotes && *idx == '\\') {
6492 STPUTC(CTLESC, expdest);
6493 len++;
6494 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006495 STPUTC(*idx, expdest);
6496 if (stackblock() != restart_detect)
6497 goto restart;
6498 len++;
6499 idx++;
6500 }
6501 break;
6502 }
6503 }
6504
6505 /* We've put the replaced text into a buffer at workloc, now
6506 * move it to the right place and adjust the stack.
6507 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006508 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006509 startp = (char *)stackblock() + startloc;
6510 memmove(startp, (char *)stackblock() + workloc, len + 1);
6511 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006512 STADJUST(-amount, expdest);
6513 return startp;
6514 }
6515#endif /* ENABLE_ASH_BASH_COMPAT */
6516
6517 subtype -= VSTRIMRIGHT;
6518#if DEBUG
6519 if (subtype < 0 || subtype > 7)
6520 abort();
6521#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006522 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006523 zero = subtype >> 1;
6524 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6525 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6526
6527 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6528 if (loc) {
6529 if (zero) {
6530 memmove(startp, loc, str - loc);
6531 loc = startp + (str - loc) - 1;
6532 }
6533 *loc = '\0';
6534 amount = loc - expdest;
6535 STADJUST(amount, expdest);
6536 }
6537 return loc;
6538}
6539
6540/*
6541 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006542 * name parameter (examples):
6543 * ash -c 'echo $1' name:'1='
6544 * ash -c 'echo $qwe' name:'qwe='
6545 * ash -c 'echo $$' name:'$='
6546 * ash -c 'echo ${$}' name:'$='
6547 * ash -c 'echo ${$##q}' name:'$=q'
6548 * ash -c 'echo ${#$}' name:'$='
6549 * note: examples with bad shell syntax:
6550 * ash -c 'echo ${#$1}' name:'$=1'
6551 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006552 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006553static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006554varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006555{
Mike Frysinger98c52642009-04-02 10:02:37 +00006556 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006557 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006558 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006559 int sepq = 0;
6560 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006561 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006562 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006563 int quoted = varflags & VSQUOTE;
6564 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006565
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006566 switch (*name) {
6567 case '$':
6568 num = rootpid;
6569 goto numvar;
6570 case '?':
6571 num = exitstatus;
6572 goto numvar;
6573 case '#':
6574 num = shellparam.nparam;
6575 goto numvar;
6576 case '!':
6577 num = backgndpid;
6578 if (num == 0)
6579 return -1;
6580 numvar:
6581 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006582 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006583 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006584 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006585 for (i = NOPTS - 1; i >= 0; i--) {
6586 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006587 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588 len++;
6589 }
6590 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006591 check_1char_name:
6592#if 0
6593 /* handles cases similar to ${#$1} */
6594 if (name[2] != '\0')
6595 raise_error_syntax("bad substitution");
6596#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006597 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006598 case '@': {
6599 char **ap;
6600 int sep;
6601
6602 if (quoted && (flags & EXP_FULL)) {
6603 /* note: this is not meant as PEOF value */
6604 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006605 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006606 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006607 /* fall through */
6608 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006609 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006610 i = SIT(sep, syntax);
6611 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006612 sepq = 1;
6613 param:
6614 ap = shellparam.p;
6615 if (!ap)
6616 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006617 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006618 size_t partlen;
6619
6620 partlen = strlen(p);
6621 len += partlen;
6622
6623 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6624 memtodest(p, partlen, syntax, quotes);
6625
6626 if (*ap && sep) {
6627 char *q;
6628
6629 len++;
6630 if (subtype == VSPLUS || subtype == VSLENGTH) {
6631 continue;
6632 }
6633 q = expdest;
6634 if (sepq)
6635 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006636 /* note: may put NUL despite sep != 0
6637 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006638 STPUTC(sep, q);
6639 expdest = q;
6640 }
6641 }
6642 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006643 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006644 case '0':
6645 case '1':
6646 case '2':
6647 case '3':
6648 case '4':
6649 case '5':
6650 case '6':
6651 case '7':
6652 case '8':
6653 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006654 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006655 if (num < 0 || num > shellparam.nparam)
6656 return -1;
6657 p = num ? shellparam.p[num - 1] : arg0;
6658 goto value;
6659 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006660 /* NB: name has form "VAR=..." */
6661
6662 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6663 * which should be considered before we check variables. */
6664 if (var_str_list) {
6665 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6666 p = NULL;
6667 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006668 char *str, *eq;
6669 str = var_str_list->text;
6670 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006671 if (!eq) /* stop at first non-assignment */
6672 break;
6673 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006674 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006675 && strncmp(str, name, name_len) == 0
6676 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006677 p = eq;
6678 /* goto value; - WRONG! */
6679 /* think "A=1 A=2 B=$A" */
6680 }
6681 var_str_list = var_str_list->next;
6682 } while (var_str_list);
6683 if (p)
6684 goto value;
6685 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006686 p = lookupvar(name);
6687 value:
6688 if (!p)
6689 return -1;
6690
6691 len = strlen(p);
6692 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6693 memtodest(p, len, syntax, quotes);
6694 return len;
6695 }
6696
6697 if (subtype == VSPLUS || subtype == VSLENGTH)
6698 STADJUST(-len, expdest);
6699 return len;
6700}
6701
6702/*
6703 * Expand a variable, and return a pointer to the next character in the
6704 * input string.
6705 */
6706static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006707evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006708{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006709 char varflags;
6710 char subtype;
6711 char quoted;
6712 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006713 char *var;
6714 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006715 int startloc;
6716 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006717
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006718 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006719 subtype = varflags & VSTYPE;
6720 quoted = varflags & VSQUOTE;
6721 var = p;
6722 easy = (!quoted || (*var == '@' && shellparam.nparam));
6723 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006724 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006725
6726 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006727 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006728 if (varflags & VSNUL)
6729 varlen--;
6730
6731 if (subtype == VSPLUS) {
6732 varlen = -1 - varlen;
6733 goto vsplus;
6734 }
6735
6736 if (subtype == VSMINUS) {
6737 vsplus:
6738 if (varlen < 0) {
6739 argstr(
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006740 p, flags | EXP_TILDE |
6741 (quoted ? EXP_QWORD : EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006742 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006743 );
6744 goto end;
6745 }
6746 if (easy)
6747 goto record;
6748 goto end;
6749 }
6750
6751 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6752 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006753 if (subevalvar(p, var, /* strloc: */ 0,
6754 subtype, startloc, varflags,
6755 /* quotes: */ 0,
6756 var_str_list)
6757 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006758 varflags &= ~VSNUL;
6759 /*
6760 * Remove any recorded regions beyond
6761 * start of variable
6762 */
6763 removerecordregions(startloc);
6764 goto again;
6765 }
6766 goto end;
6767 }
6768 if (easy)
6769 goto record;
6770 goto end;
6771 }
6772
6773 if (varlen < 0 && uflag)
6774 varunset(p, var, 0, 0);
6775
6776 if (subtype == VSLENGTH) {
6777 cvtnum(varlen > 0 ? varlen : 0);
6778 goto record;
6779 }
6780
6781 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006782 if (easy)
6783 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006784 goto end;
6785 }
6786
6787#if DEBUG
6788 switch (subtype) {
6789 case VSTRIMLEFT:
6790 case VSTRIMLEFTMAX:
6791 case VSTRIMRIGHT:
6792 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006793#if ENABLE_ASH_BASH_COMPAT
6794 case VSSUBSTR:
6795 case VSREPLACE:
6796 case VSREPLACEALL:
6797#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006798 break;
6799 default:
6800 abort();
6801 }
6802#endif
6803
6804 if (varlen >= 0) {
6805 /*
6806 * Terminate the string and start recording the pattern
6807 * right after it
6808 */
6809 STPUTC('\0', expdest);
6810 patloc = expdest - (char *)stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006811 if (NULL == subevalvar(p, /* str: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006812 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006813//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006814 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006815 var_str_list)
6816 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006817 int amount = expdest - (
6818 (char *)stackblock() + patloc - 1
6819 );
6820 STADJUST(-amount, expdest);
6821 }
6822 /* Remove any recorded regions beyond start of variable */
6823 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006824 record:
6825 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006826 }
6827
6828 end:
6829 if (subtype != VSNORMAL) { /* skip to end of alternative */
6830 int nesting = 1;
6831 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006832 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006833 if (c == CTLESC)
6834 p++;
6835 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6836 if (varlen >= 0)
6837 argbackq = argbackq->next;
6838 } else if (c == CTLVAR) {
6839 if ((*p++ & VSTYPE) != VSNORMAL)
6840 nesting++;
6841 } else if (c == CTLENDVAR) {
6842 if (--nesting == 0)
6843 break;
6844 }
6845 }
6846 }
6847 return p;
6848}
6849
6850/*
6851 * Break the argument string into pieces based upon IFS and add the
6852 * strings to the argument list. The regions of the string to be
6853 * searched for IFS characters have been stored by recordregion.
6854 */
6855static void
6856ifsbreakup(char *string, struct arglist *arglist)
6857{
6858 struct ifsregion *ifsp;
6859 struct strlist *sp;
6860 char *start;
6861 char *p;
6862 char *q;
6863 const char *ifs, *realifs;
6864 int ifsspc;
6865 int nulonly;
6866
6867 start = string;
6868 if (ifslastp != NULL) {
6869 ifsspc = 0;
6870 nulonly = 0;
6871 realifs = ifsset() ? ifsval() : defifs;
6872 ifsp = &ifsfirst;
6873 do {
6874 p = string + ifsp->begoff;
6875 nulonly = ifsp->nulonly;
6876 ifs = nulonly ? nullstr : realifs;
6877 ifsspc = 0;
6878 while (p < string + ifsp->endoff) {
6879 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006880 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006881 p++;
6882 if (!strchr(ifs, *p)) {
6883 p++;
6884 continue;
6885 }
6886 if (!nulonly)
6887 ifsspc = (strchr(defifs, *p) != NULL);
6888 /* Ignore IFS whitespace at start */
6889 if (q == start && ifsspc) {
6890 p++;
6891 start = p;
6892 continue;
6893 }
6894 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006895 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006896 sp->text = start;
6897 *arglist->lastp = sp;
6898 arglist->lastp = &sp->next;
6899 p++;
6900 if (!nulonly) {
6901 for (;;) {
6902 if (p >= string + ifsp->endoff) {
6903 break;
6904 }
6905 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006906 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006907 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006908 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006909 p = q;
6910 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006911 }
6912 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006913 if (ifsspc) {
6914 p++;
6915 ifsspc = 0;
6916 } else {
6917 p = q;
6918 break;
6919 }
6920 } else
6921 p++;
6922 }
6923 }
6924 start = p;
6925 } /* while */
6926 ifsp = ifsp->next;
6927 } while (ifsp != NULL);
6928 if (nulonly)
6929 goto add;
6930 }
6931
6932 if (!*start)
6933 return;
6934
6935 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006936 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006937 sp->text = start;
6938 *arglist->lastp = sp;
6939 arglist->lastp = &sp->next;
6940}
6941
6942static void
6943ifsfree(void)
6944{
6945 struct ifsregion *p;
6946
6947 INT_OFF;
6948 p = ifsfirst.next;
6949 do {
6950 struct ifsregion *ifsp;
6951 ifsp = p->next;
6952 free(p);
6953 p = ifsp;
6954 } while (p);
6955 ifslastp = NULL;
6956 ifsfirst.next = NULL;
6957 INT_ON;
6958}
6959
6960/*
6961 * Add a file name to the list.
6962 */
6963static void
6964addfname(const char *name)
6965{
6966 struct strlist *sp;
6967
Denis Vlasenko597906c2008-02-20 16:38:54 +00006968 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006969 sp->text = ststrdup(name);
6970 *exparg.lastp = sp;
6971 exparg.lastp = &sp->next;
6972}
6973
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006974/*
6975 * Do metacharacter (i.e. *, ?, [...]) expansion.
6976 */
6977static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006978expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006979{
6980 char *p;
6981 const char *cp;
6982 char *start;
6983 char *endname;
6984 int metaflag;
6985 struct stat statb;
6986 DIR *dirp;
6987 struct dirent *dp;
6988 int atend;
6989 int matchdot;
6990
6991 metaflag = 0;
6992 start = name;
6993 for (p = name; *p; p++) {
6994 if (*p == '*' || *p == '?')
6995 metaflag = 1;
6996 else if (*p == '[') {
6997 char *q = p + 1;
6998 if (*q == '!')
6999 q++;
7000 for (;;) {
7001 if (*q == '\\')
7002 q++;
7003 if (*q == '/' || *q == '\0')
7004 break;
7005 if (*++q == ']') {
7006 metaflag = 1;
7007 break;
7008 }
7009 }
7010 } else if (*p == '\\')
7011 p++;
7012 else if (*p == '/') {
7013 if (metaflag)
7014 goto out;
7015 start = p + 1;
7016 }
7017 }
7018 out:
7019 if (metaflag == 0) { /* we've reached the end of the file name */
7020 if (enddir != expdir)
7021 metaflag++;
7022 p = name;
7023 do {
7024 if (*p == '\\')
7025 p++;
7026 *enddir++ = *p;
7027 } while (*p++);
7028 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7029 addfname(expdir);
7030 return;
7031 }
7032 endname = p;
7033 if (name < start) {
7034 p = name;
7035 do {
7036 if (*p == '\\')
7037 p++;
7038 *enddir++ = *p++;
7039 } while (p < start);
7040 }
7041 if (enddir == expdir) {
7042 cp = ".";
7043 } else if (enddir == expdir + 1 && *expdir == '/') {
7044 cp = "/";
7045 } else {
7046 cp = expdir;
7047 enddir[-1] = '\0';
7048 }
7049 dirp = opendir(cp);
7050 if (dirp == NULL)
7051 return;
7052 if (enddir != expdir)
7053 enddir[-1] = '/';
7054 if (*endname == 0) {
7055 atend = 1;
7056 } else {
7057 atend = 0;
7058 *endname++ = '\0';
7059 }
7060 matchdot = 0;
7061 p = start;
7062 if (*p == '\\')
7063 p++;
7064 if (*p == '.')
7065 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007066 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007067 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007068 continue;
7069 if (pmatch(start, dp->d_name)) {
7070 if (atend) {
7071 strcpy(enddir, dp->d_name);
7072 addfname(expdir);
7073 } else {
7074 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7075 continue;
7076 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007077 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007078 }
7079 }
7080 }
7081 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007082 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007083 endname[-1] = '/';
7084}
7085
7086static struct strlist *
7087msort(struct strlist *list, int len)
7088{
7089 struct strlist *p, *q = NULL;
7090 struct strlist **lpp;
7091 int half;
7092 int n;
7093
7094 if (len <= 1)
7095 return list;
7096 half = len >> 1;
7097 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007098 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007099 q = p;
7100 p = p->next;
7101 }
7102 q->next = NULL; /* terminate first half of list */
7103 q = msort(list, half); /* sort first half of list */
7104 p = msort(p, len - half); /* sort second half */
7105 lpp = &list;
7106 for (;;) {
7107#if ENABLE_LOCALE_SUPPORT
7108 if (strcoll(p->text, q->text) < 0)
7109#else
7110 if (strcmp(p->text, q->text) < 0)
7111#endif
7112 {
7113 *lpp = p;
7114 lpp = &p->next;
7115 p = *lpp;
7116 if (p == NULL) {
7117 *lpp = q;
7118 break;
7119 }
7120 } else {
7121 *lpp = q;
7122 lpp = &q->next;
7123 q = *lpp;
7124 if (q == NULL) {
7125 *lpp = p;
7126 break;
7127 }
7128 }
7129 }
7130 return list;
7131}
7132
7133/*
7134 * Sort the results of file name expansion. It calculates the number of
7135 * strings to sort and then calls msort (short for merge sort) to do the
7136 * work.
7137 */
7138static struct strlist *
7139expsort(struct strlist *str)
7140{
7141 int len;
7142 struct strlist *sp;
7143
7144 len = 0;
7145 for (sp = str; sp; sp = sp->next)
7146 len++;
7147 return msort(str, len);
7148}
7149
7150static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007151expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007152{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007153 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007154 '*', '?', '[', 0
7155 };
7156 /* TODO - EXP_REDIR */
7157
7158 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007159 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007160 struct strlist **savelastp;
7161 struct strlist *sp;
7162 char *p;
7163
7164 if (fflag)
7165 goto nometa;
7166 if (!strpbrk(str->text, metachars))
7167 goto nometa;
7168 savelastp = exparg.lastp;
7169
7170 INT_OFF;
7171 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7172 {
7173 int i = strlen(str->text);
7174 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7175 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007176 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007177 free(expdir);
7178 if (p != str->text)
7179 free(p);
7180 INT_ON;
7181 if (exparg.lastp == savelastp) {
7182 /*
7183 * no matches
7184 */
7185 nometa:
7186 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007187 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007188 exparg.lastp = &str->next;
7189 } else {
7190 *exparg.lastp = NULL;
7191 *savelastp = sp = expsort(*savelastp);
7192 while (sp->next != NULL)
7193 sp = sp->next;
7194 exparg.lastp = &sp->next;
7195 }
7196 str = str->next;
7197 }
7198}
7199
7200/*
7201 * Perform variable substitution and command substitution on an argument,
7202 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7203 * perform splitting and file name expansion. When arglist is NULL, perform
7204 * here document expansion.
7205 */
7206static void
7207expandarg(union node *arg, struct arglist *arglist, int flag)
7208{
7209 struct strlist *sp;
7210 char *p;
7211
7212 argbackq = arg->narg.backquote;
7213 STARTSTACKSTR(expdest);
7214 ifsfirst.next = NULL;
7215 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007216 argstr(arg->narg.text, flag,
7217 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007218 p = _STPUTC('\0', expdest);
7219 expdest = p - 1;
7220 if (arglist == NULL) {
7221 return; /* here document expanded */
7222 }
7223 p = grabstackstr(p);
7224 exparg.lastp = &exparg.list;
7225 /*
7226 * TODO - EXP_REDIR
7227 */
7228 if (flag & EXP_FULL) {
7229 ifsbreakup(p, &exparg);
7230 *exparg.lastp = NULL;
7231 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007232 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007233 } else {
7234 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007235 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007236 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007237 sp->text = p;
7238 *exparg.lastp = sp;
7239 exparg.lastp = &sp->next;
7240 }
7241 if (ifsfirst.next)
7242 ifsfree();
7243 *exparg.lastp = NULL;
7244 if (exparg.list) {
7245 *arglist->lastp = exparg.list;
7246 arglist->lastp = exparg.lastp;
7247 }
7248}
7249
7250/*
7251 * Expand shell variables and backquotes inside a here document.
7252 */
7253static void
7254expandhere(union node *arg, int fd)
7255{
7256 herefd = fd;
7257 expandarg(arg, (struct arglist *)NULL, 0);
7258 full_write(fd, stackblock(), expdest - (char *)stackblock());
7259}
7260
7261/*
7262 * Returns true if the pattern matches the string.
7263 */
7264static int
7265patmatch(char *pattern, const char *string)
7266{
7267 return pmatch(preglob(pattern, 0, 0), string);
7268}
7269
7270/*
7271 * See if a pattern matches in a case statement.
7272 */
7273static int
7274casematch(union node *pattern, char *val)
7275{
7276 struct stackmark smark;
7277 int result;
7278
7279 setstackmark(&smark);
7280 argbackq = pattern->narg.backquote;
7281 STARTSTACKSTR(expdest);
7282 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007283 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7284 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007285 STACKSTRNUL(expdest);
7286 result = patmatch(stackblock(), val);
7287 popstackmark(&smark);
7288 return result;
7289}
7290
7291
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007292/* ============ find_command */
7293
7294struct builtincmd {
7295 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007296 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007297 /* unsigned flags; */
7298};
7299#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007300/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007301 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007302#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007303#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007304
7305struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007306 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007307 union param {
7308 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007309 /* index >= 0 for commands without path (slashes) */
7310 /* (TODO: what exactly does the value mean? PATH position?) */
7311 /* index == -1 for commands with slashes */
7312 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007313 const struct builtincmd *cmd;
7314 struct funcnode *func;
7315 } u;
7316};
7317/* values of cmdtype */
7318#define CMDUNKNOWN -1 /* no entry in table for command */
7319#define CMDNORMAL 0 /* command is an executable program */
7320#define CMDFUNCTION 1 /* command is a shell function */
7321#define CMDBUILTIN 2 /* command is a shell builtin */
7322
7323/* action to find_command() */
7324#define DO_ERR 0x01 /* prints errors */
7325#define DO_ABS 0x02 /* checks absolute paths */
7326#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7327#define DO_ALTPATH 0x08 /* using alternate path */
7328#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7329
7330static void find_command(char *, struct cmdentry *, int, const char *);
7331
7332
7333/* ============ Hashing commands */
7334
7335/*
7336 * When commands are first encountered, they are entered in a hash table.
7337 * This ensures that a full path search will not have to be done for them
7338 * on each invocation.
7339 *
7340 * We should investigate converting to a linear search, even though that
7341 * would make the command name "hash" a misnomer.
7342 */
7343
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007344struct tblentry {
7345 struct tblentry *next; /* next entry in hash chain */
7346 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007347 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007348 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007349 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007350};
7351
Denis Vlasenko01631112007-12-16 17:20:38 +00007352static struct tblentry **cmdtable;
7353#define INIT_G_cmdtable() do { \
7354 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7355} while (0)
7356
7357static int builtinloc = -1; /* index in path of %builtin, or -1 */
7358
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007359
7360static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007361tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007362{
7363 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007364
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007365#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007366 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007367 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007368 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007369 while (*envp)
7370 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007371 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007372 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007373 /* re-exec ourselves with the new arguments */
7374 execve(bb_busybox_exec_path, argv, envp);
7375 /* If they called chroot or otherwise made the binary no longer
7376 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007377 }
7378#endif
7379
7380 repeat:
7381#ifdef SYSV
7382 do {
7383 execve(cmd, argv, envp);
7384 } while (errno == EINTR);
7385#else
7386 execve(cmd, argv, envp);
7387#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007388 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007389 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007390 return;
7391 }
7392 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007393 char **ap;
7394 char **new;
7395
7396 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007397 continue;
7398 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007399 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007400 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401 ap += 2;
7402 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007403 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007404 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007405 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007406 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007407 goto repeat;
7408 }
7409}
7410
7411/*
7412 * Exec a program. Never returns. If you change this routine, you may
7413 * have to change the find_command routine as well.
7414 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007415static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007416static void
7417shellexec(char **argv, const char *path, int idx)
7418{
7419 char *cmdname;
7420 int e;
7421 char **envp;
7422 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007423#if ENABLE_FEATURE_SH_STANDALONE
7424 int applet_no = -1;
7425#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007426
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007427 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007428 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007429 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007430#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007431 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007432#endif
7433 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007434 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007435 e = errno;
7436 } else {
7437 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007438 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007439 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007440 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007441 if (errno != ENOENT && errno != ENOTDIR)
7442 e = errno;
7443 }
7444 stunalloc(cmdname);
7445 }
7446 }
7447
7448 /* Map to POSIX errors */
7449 switch (e) {
7450 case EACCES:
7451 exerrno = 126;
7452 break;
7453 case ENOENT:
7454 exerrno = 127;
7455 break;
7456 default:
7457 exerrno = 2;
7458 break;
7459 }
7460 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007461 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7462 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007463 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7464 /* NOTREACHED */
7465}
7466
7467static void
7468printentry(struct tblentry *cmdp)
7469{
7470 int idx;
7471 const char *path;
7472 char *name;
7473
7474 idx = cmdp->param.index;
7475 path = pathval();
7476 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007477 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007478 stunalloc(name);
7479 } while (--idx >= 0);
7480 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7481}
7482
7483/*
7484 * Clear out command entries. The argument specifies the first entry in
7485 * PATH which has changed.
7486 */
7487static void
7488clearcmdentry(int firstchange)
7489{
7490 struct tblentry **tblp;
7491 struct tblentry **pp;
7492 struct tblentry *cmdp;
7493
7494 INT_OFF;
7495 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7496 pp = tblp;
7497 while ((cmdp = *pp) != NULL) {
7498 if ((cmdp->cmdtype == CMDNORMAL &&
7499 cmdp->param.index >= firstchange)
7500 || (cmdp->cmdtype == CMDBUILTIN &&
7501 builtinloc >= firstchange)
7502 ) {
7503 *pp = cmdp->next;
7504 free(cmdp);
7505 } else {
7506 pp = &cmdp->next;
7507 }
7508 }
7509 }
7510 INT_ON;
7511}
7512
7513/*
7514 * Locate a command in the command hash table. If "add" is nonzero,
7515 * add the command to the table if it is not already present. The
7516 * variable "lastcmdentry" is set to point to the address of the link
7517 * pointing to the entry, so that delete_cmd_entry can delete the
7518 * entry.
7519 *
7520 * Interrupts must be off if called with add != 0.
7521 */
7522static struct tblentry **lastcmdentry;
7523
7524static struct tblentry *
7525cmdlookup(const char *name, int add)
7526{
7527 unsigned int hashval;
7528 const char *p;
7529 struct tblentry *cmdp;
7530 struct tblentry **pp;
7531
7532 p = name;
7533 hashval = (unsigned char)*p << 4;
7534 while (*p)
7535 hashval += (unsigned char)*p++;
7536 hashval &= 0x7FFF;
7537 pp = &cmdtable[hashval % CMDTABLESIZE];
7538 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7539 if (strcmp(cmdp->cmdname, name) == 0)
7540 break;
7541 pp = &cmdp->next;
7542 }
7543 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007544 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7545 + strlen(name)
7546 /* + 1 - already done because
7547 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007548 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007549 cmdp->cmdtype = CMDUNKNOWN;
7550 strcpy(cmdp->cmdname, name);
7551 }
7552 lastcmdentry = pp;
7553 return cmdp;
7554}
7555
7556/*
7557 * Delete the command entry returned on the last lookup.
7558 */
7559static void
7560delete_cmd_entry(void)
7561{
7562 struct tblentry *cmdp;
7563
7564 INT_OFF;
7565 cmdp = *lastcmdentry;
7566 *lastcmdentry = cmdp->next;
7567 if (cmdp->cmdtype == CMDFUNCTION)
7568 freefunc(cmdp->param.func);
7569 free(cmdp);
7570 INT_ON;
7571}
7572
7573/*
7574 * Add a new command entry, replacing any existing command entry for
7575 * the same name - except special builtins.
7576 */
7577static void
7578addcmdentry(char *name, struct cmdentry *entry)
7579{
7580 struct tblentry *cmdp;
7581
7582 cmdp = cmdlookup(name, 1);
7583 if (cmdp->cmdtype == CMDFUNCTION) {
7584 freefunc(cmdp->param.func);
7585 }
7586 cmdp->cmdtype = entry->cmdtype;
7587 cmdp->param = entry->u;
7588 cmdp->rehash = 0;
7589}
7590
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007591static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007592hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007593{
7594 struct tblentry **pp;
7595 struct tblentry *cmdp;
7596 int c;
7597 struct cmdentry entry;
7598 char *name;
7599
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007600 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007601 clearcmdentry(0);
7602 return 0;
7603 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007604
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007605 if (*argptr == NULL) {
7606 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7607 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7608 if (cmdp->cmdtype == CMDNORMAL)
7609 printentry(cmdp);
7610 }
7611 }
7612 return 0;
7613 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007614
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007615 c = 0;
7616 while ((name = *argptr) != NULL) {
7617 cmdp = cmdlookup(name, 0);
7618 if (cmdp != NULL
7619 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007620 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7621 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007622 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007623 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007624 find_command(name, &entry, DO_ERR, pathval());
7625 if (entry.cmdtype == CMDUNKNOWN)
7626 c = 1;
7627 argptr++;
7628 }
7629 return c;
7630}
7631
7632/*
7633 * Called when a cd is done. Marks all commands so the next time they
7634 * are executed they will be rehashed.
7635 */
7636static void
7637hashcd(void)
7638{
7639 struct tblentry **pp;
7640 struct tblentry *cmdp;
7641
7642 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7643 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007644 if (cmdp->cmdtype == CMDNORMAL
7645 || (cmdp->cmdtype == CMDBUILTIN
7646 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7647 && builtinloc > 0)
7648 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007649 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007650 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007651 }
7652 }
7653}
7654
7655/*
7656 * Fix command hash table when PATH changed.
7657 * Called before PATH is changed. The argument is the new value of PATH;
7658 * pathval() still returns the old value at this point.
7659 * Called with interrupts off.
7660 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007661static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007662changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007663{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007664 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007665 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007666 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007667 int idx_bltin;
7668
7669 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007670 firstchange = 9999; /* assume no change */
7671 idx = 0;
7672 idx_bltin = -1;
7673 for (;;) {
7674 if (*old != *new) {
7675 firstchange = idx;
7676 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007677 || (*old == ':' && *new == '\0')
7678 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007679 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007680 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007681 old = new; /* ignore subsequent differences */
7682 }
7683 if (*new == '\0')
7684 break;
7685 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7686 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007687 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007688 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007689 new++;
7690 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007691 }
7692 if (builtinloc < 0 && idx_bltin >= 0)
7693 builtinloc = idx_bltin; /* zap builtins */
7694 if (builtinloc >= 0 && idx_bltin < 0)
7695 firstchange = 0;
7696 clearcmdentry(firstchange);
7697 builtinloc = idx_bltin;
7698}
7699
7700#define TEOF 0
7701#define TNL 1
7702#define TREDIR 2
7703#define TWORD 3
7704#define TSEMI 4
7705#define TBACKGND 5
7706#define TAND 6
7707#define TOR 7
7708#define TPIPE 8
7709#define TLP 9
7710#define TRP 10
7711#define TENDCASE 11
7712#define TENDBQUOTE 12
7713#define TNOT 13
7714#define TCASE 14
7715#define TDO 15
7716#define TDONE 16
7717#define TELIF 17
7718#define TELSE 18
7719#define TESAC 19
7720#define TFI 20
7721#define TFOR 21
7722#define TIF 22
7723#define TIN 23
7724#define TTHEN 24
7725#define TUNTIL 25
7726#define TWHILE 26
7727#define TBEGIN 27
7728#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007729typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007730
7731/* first char is indicating which tokens mark the end of a list */
7732static const char *const tokname_array[] = {
7733 "\1end of file",
7734 "\0newline",
7735 "\0redirection",
7736 "\0word",
7737 "\0;",
7738 "\0&",
7739 "\0&&",
7740 "\0||",
7741 "\0|",
7742 "\0(",
7743 "\1)",
7744 "\1;;",
7745 "\1`",
7746#define KWDOFFSET 13
7747 /* the following are keywords */
7748 "\0!",
7749 "\0case",
7750 "\1do",
7751 "\1done",
7752 "\1elif",
7753 "\1else",
7754 "\1esac",
7755 "\1fi",
7756 "\0for",
7757 "\0if",
7758 "\0in",
7759 "\1then",
7760 "\0until",
7761 "\0while",
7762 "\0{",
7763 "\1}",
7764};
7765
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007766/* Wrapper around strcmp for qsort/bsearch/... */
7767static int
7768pstrcmp(const void *a, const void *b)
7769{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007770 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007771}
7772
7773static const char *const *
7774findkwd(const char *s)
7775{
7776 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007777 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7778 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007779}
7780
7781/*
7782 * Locate and print what a word is...
7783 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007784static int
7785describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007786{
7787 struct cmdentry entry;
7788 struct tblentry *cmdp;
7789#if ENABLE_ASH_ALIAS
7790 const struct alias *ap;
7791#endif
7792 const char *path = pathval();
7793
7794 if (describe_command_verbose) {
7795 out1str(command);
7796 }
7797
7798 /* First look at the keywords */
7799 if (findkwd(command)) {
7800 out1str(describe_command_verbose ? " is a shell keyword" : command);
7801 goto out;
7802 }
7803
7804#if ENABLE_ASH_ALIAS
7805 /* Then look at the aliases */
7806 ap = lookupalias(command, 0);
7807 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007808 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007809 out1str("alias ");
7810 printalias(ap);
7811 return 0;
7812 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007813 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007814 goto out;
7815 }
7816#endif
7817 /* Then check if it is a tracked alias */
7818 cmdp = cmdlookup(command, 0);
7819 if (cmdp != NULL) {
7820 entry.cmdtype = cmdp->cmdtype;
7821 entry.u = cmdp->param;
7822 } else {
7823 /* Finally use brute force */
7824 find_command(command, &entry, DO_ABS, path);
7825 }
7826
7827 switch (entry.cmdtype) {
7828 case CMDNORMAL: {
7829 int j = entry.u.index;
7830 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007831 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007832 p = command;
7833 } else {
7834 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007835 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007836 stunalloc(p);
7837 } while (--j >= 0);
7838 }
7839 if (describe_command_verbose) {
7840 out1fmt(" is%s %s",
7841 (cmdp ? " a tracked alias for" : nullstr), p
7842 );
7843 } else {
7844 out1str(p);
7845 }
7846 break;
7847 }
7848
7849 case CMDFUNCTION:
7850 if (describe_command_verbose) {
7851 out1str(" is a shell function");
7852 } else {
7853 out1str(command);
7854 }
7855 break;
7856
7857 case CMDBUILTIN:
7858 if (describe_command_verbose) {
7859 out1fmt(" is a %sshell builtin",
7860 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7861 "special " : nullstr
7862 );
7863 } else {
7864 out1str(command);
7865 }
7866 break;
7867
7868 default:
7869 if (describe_command_verbose) {
7870 out1str(": not found\n");
7871 }
7872 return 127;
7873 }
7874 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007875 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007876 return 0;
7877}
7878
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007879static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007880typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007881{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007882 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007883 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007884 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007885
Denis Vlasenko46846e22007-05-20 13:08:31 +00007886 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007887 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007888 i++;
7889 verbose = 0;
7890 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007891 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007892 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007893 }
7894 return err;
7895}
7896
7897#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007898static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007899commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007900{
7901 int c;
7902 enum {
7903 VERIFY_BRIEF = 1,
7904 VERIFY_VERBOSE = 2,
7905 } verify = 0;
7906
7907 while ((c = nextopt("pvV")) != '\0')
7908 if (c == 'V')
7909 verify |= VERIFY_VERBOSE;
7910 else if (c == 'v')
7911 verify |= VERIFY_BRIEF;
7912#if DEBUG
7913 else if (c != 'p')
7914 abort();
7915#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007916 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7917 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007918 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007919 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007920
7921 return 0;
7922}
7923#endif
7924
7925
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007926/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007927
Denis Vlasenko340299a2008-11-21 10:36:36 +00007928static int funcblocksize; /* size of structures in function */
7929static int funcstringsize; /* size of strings in node */
7930static void *funcblock; /* block to allocate function from */
7931static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007932
Eric Andersencb57d552001-06-28 07:25:16 +00007933/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007934#define EV_EXIT 01 /* exit after evaluating tree */
7935#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007936#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007937
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007938static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007939 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7940 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7941 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7942 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7943 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7944 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7945 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7946 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7947 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7948 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7949 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7950 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7951 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7952 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7953 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7954 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7955 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007956#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007957 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007958#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007959 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7960 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7961 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7962 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7963 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7964 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7965 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7966 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7967 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007968};
7969
7970static void calcsize(union node *n);
7971
7972static void
7973sizenodelist(struct nodelist *lp)
7974{
7975 while (lp) {
7976 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7977 calcsize(lp->n);
7978 lp = lp->next;
7979 }
7980}
7981
7982static void
7983calcsize(union node *n)
7984{
7985 if (n == NULL)
7986 return;
7987 funcblocksize += nodesize[n->type];
7988 switch (n->type) {
7989 case NCMD:
7990 calcsize(n->ncmd.redirect);
7991 calcsize(n->ncmd.args);
7992 calcsize(n->ncmd.assign);
7993 break;
7994 case NPIPE:
7995 sizenodelist(n->npipe.cmdlist);
7996 break;
7997 case NREDIR:
7998 case NBACKGND:
7999 case NSUBSHELL:
8000 calcsize(n->nredir.redirect);
8001 calcsize(n->nredir.n);
8002 break;
8003 case NAND:
8004 case NOR:
8005 case NSEMI:
8006 case NWHILE:
8007 case NUNTIL:
8008 calcsize(n->nbinary.ch2);
8009 calcsize(n->nbinary.ch1);
8010 break;
8011 case NIF:
8012 calcsize(n->nif.elsepart);
8013 calcsize(n->nif.ifpart);
8014 calcsize(n->nif.test);
8015 break;
8016 case NFOR:
8017 funcstringsize += strlen(n->nfor.var) + 1;
8018 calcsize(n->nfor.body);
8019 calcsize(n->nfor.args);
8020 break;
8021 case NCASE:
8022 calcsize(n->ncase.cases);
8023 calcsize(n->ncase.expr);
8024 break;
8025 case NCLIST:
8026 calcsize(n->nclist.body);
8027 calcsize(n->nclist.pattern);
8028 calcsize(n->nclist.next);
8029 break;
8030 case NDEFUN:
8031 case NARG:
8032 sizenodelist(n->narg.backquote);
8033 funcstringsize += strlen(n->narg.text) + 1;
8034 calcsize(n->narg.next);
8035 break;
8036 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008037#if ENABLE_ASH_BASH_COMPAT
8038 case NTO2:
8039#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008040 case NCLOBBER:
8041 case NFROM:
8042 case NFROMTO:
8043 case NAPPEND:
8044 calcsize(n->nfile.fname);
8045 calcsize(n->nfile.next);
8046 break;
8047 case NTOFD:
8048 case NFROMFD:
8049 calcsize(n->ndup.vname);
8050 calcsize(n->ndup.next);
8051 break;
8052 case NHERE:
8053 case NXHERE:
8054 calcsize(n->nhere.doc);
8055 calcsize(n->nhere.next);
8056 break;
8057 case NNOT:
8058 calcsize(n->nnot.com);
8059 break;
8060 };
8061}
8062
8063static char *
8064nodeckstrdup(char *s)
8065{
8066 char *rtn = funcstring;
8067
8068 strcpy(funcstring, s);
8069 funcstring += strlen(s) + 1;
8070 return rtn;
8071}
8072
8073static union node *copynode(union node *);
8074
8075static struct nodelist *
8076copynodelist(struct nodelist *lp)
8077{
8078 struct nodelist *start;
8079 struct nodelist **lpp;
8080
8081 lpp = &start;
8082 while (lp) {
8083 *lpp = funcblock;
8084 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8085 (*lpp)->n = copynode(lp->n);
8086 lp = lp->next;
8087 lpp = &(*lpp)->next;
8088 }
8089 *lpp = NULL;
8090 return start;
8091}
8092
8093static union node *
8094copynode(union node *n)
8095{
8096 union node *new;
8097
8098 if (n == NULL)
8099 return NULL;
8100 new = funcblock;
8101 funcblock = (char *) funcblock + nodesize[n->type];
8102
8103 switch (n->type) {
8104 case NCMD:
8105 new->ncmd.redirect = copynode(n->ncmd.redirect);
8106 new->ncmd.args = copynode(n->ncmd.args);
8107 new->ncmd.assign = copynode(n->ncmd.assign);
8108 break;
8109 case NPIPE:
8110 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008111 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008112 break;
8113 case NREDIR:
8114 case NBACKGND:
8115 case NSUBSHELL:
8116 new->nredir.redirect = copynode(n->nredir.redirect);
8117 new->nredir.n = copynode(n->nredir.n);
8118 break;
8119 case NAND:
8120 case NOR:
8121 case NSEMI:
8122 case NWHILE:
8123 case NUNTIL:
8124 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8125 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8126 break;
8127 case NIF:
8128 new->nif.elsepart = copynode(n->nif.elsepart);
8129 new->nif.ifpart = copynode(n->nif.ifpart);
8130 new->nif.test = copynode(n->nif.test);
8131 break;
8132 case NFOR:
8133 new->nfor.var = nodeckstrdup(n->nfor.var);
8134 new->nfor.body = copynode(n->nfor.body);
8135 new->nfor.args = copynode(n->nfor.args);
8136 break;
8137 case NCASE:
8138 new->ncase.cases = copynode(n->ncase.cases);
8139 new->ncase.expr = copynode(n->ncase.expr);
8140 break;
8141 case NCLIST:
8142 new->nclist.body = copynode(n->nclist.body);
8143 new->nclist.pattern = copynode(n->nclist.pattern);
8144 new->nclist.next = copynode(n->nclist.next);
8145 break;
8146 case NDEFUN:
8147 case NARG:
8148 new->narg.backquote = copynodelist(n->narg.backquote);
8149 new->narg.text = nodeckstrdup(n->narg.text);
8150 new->narg.next = copynode(n->narg.next);
8151 break;
8152 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008153#if ENABLE_ASH_BASH_COMPAT
8154 case NTO2:
8155#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008156 case NCLOBBER:
8157 case NFROM:
8158 case NFROMTO:
8159 case NAPPEND:
8160 new->nfile.fname = copynode(n->nfile.fname);
8161 new->nfile.fd = n->nfile.fd;
8162 new->nfile.next = copynode(n->nfile.next);
8163 break;
8164 case NTOFD:
8165 case NFROMFD:
8166 new->ndup.vname = copynode(n->ndup.vname);
8167 new->ndup.dupfd = n->ndup.dupfd;
8168 new->ndup.fd = n->ndup.fd;
8169 new->ndup.next = copynode(n->ndup.next);
8170 break;
8171 case NHERE:
8172 case NXHERE:
8173 new->nhere.doc = copynode(n->nhere.doc);
8174 new->nhere.fd = n->nhere.fd;
8175 new->nhere.next = copynode(n->nhere.next);
8176 break;
8177 case NNOT:
8178 new->nnot.com = copynode(n->nnot.com);
8179 break;
8180 };
8181 new->type = n->type;
8182 return new;
8183}
8184
8185/*
8186 * Make a copy of a parse tree.
8187 */
8188static struct funcnode *
8189copyfunc(union node *n)
8190{
8191 struct funcnode *f;
8192 size_t blocksize;
8193
8194 funcblocksize = offsetof(struct funcnode, n);
8195 funcstringsize = 0;
8196 calcsize(n);
8197 blocksize = funcblocksize;
8198 f = ckmalloc(blocksize + funcstringsize);
8199 funcblock = (char *) f + offsetof(struct funcnode, n);
8200 funcstring = (char *) f + blocksize;
8201 copynode(n);
8202 f->count = 0;
8203 return f;
8204}
8205
8206/*
8207 * Define a shell function.
8208 */
8209static void
8210defun(char *name, union node *func)
8211{
8212 struct cmdentry entry;
8213
8214 INT_OFF;
8215 entry.cmdtype = CMDFUNCTION;
8216 entry.u.func = copyfunc(func);
8217 addcmdentry(name, &entry);
8218 INT_ON;
8219}
8220
Denis Vlasenko4b875702009-03-19 13:30:04 +00008221/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008222#define SKIPBREAK (1 << 0)
8223#define SKIPCONT (1 << 1)
8224#define SKIPFUNC (1 << 2)
8225#define SKIPFILE (1 << 3)
8226#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008227static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008228static int skipcount; /* number of levels to skip */
8229static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008230static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008231
Denis Vlasenko4b875702009-03-19 13:30:04 +00008232/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008233static int evalstring(char *s, int mask);
8234
Denis Vlasenko4b875702009-03-19 13:30:04 +00008235/* Called to execute a trap.
8236 * Single callsite - at the end of evaltree().
8237 * If we return non-zero, exaltree raises EXEXIT exception.
8238 *
8239 * Perhaps we should avoid entering new trap handlers
8240 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008241 */
8242static int
8243dotrap(void)
8244{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008245 uint8_t *g;
8246 int sig;
8247 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008248
8249 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008250 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008251 xbarrier();
8252
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008253 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008254 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8255 int want_exexit;
8256 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008257
Denis Vlasenko4b875702009-03-19 13:30:04 +00008258 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008259 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008260 t = trap[sig];
8261 /* non-trapped SIGINT is handled separately by raise_interrupt,
8262 * don't upset it by resetting gotsig[SIGINT-1] */
8263 if (sig == SIGINT && !t)
8264 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008265
8266 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008267 *g = 0;
8268 if (!t)
8269 continue;
8270 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008271 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008272 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008273 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008274 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008275 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008276 }
8277
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008278 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008279 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008280}
8281
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008282/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008283static void evalloop(union node *, int);
8284static void evalfor(union node *, int);
8285static void evalcase(union node *, int);
8286static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008287static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008288static void evalpipe(union node *, int);
8289static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008290static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008291static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008292
Eric Andersen62483552001-07-10 06:09:16 +00008293/*
Eric Andersenc470f442003-07-28 09:56:35 +00008294 * Evaluate a parse tree. The value is left in the global variable
8295 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008296 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008297static void
Eric Andersenc470f442003-07-28 09:56:35 +00008298evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008299{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008300 struct jmploc *volatile savehandler = exception_handler;
8301 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008302 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008303 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008304 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008305 int int_level;
8306
8307 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008308
Eric Andersenc470f442003-07-28 09:56:35 +00008309 if (n == NULL) {
8310 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008311 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008312 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008313 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008314
8315 exception_handler = &jmploc;
8316 {
8317 int err = setjmp(jmploc.loc);
8318 if (err) {
8319 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008320 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008321 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8322 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008323 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008324 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008325 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008326 TRACE(("exception %d in evaltree, propagating err=%d\n",
8327 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008328 exception_handler = savehandler;
8329 longjmp(exception_handler->loc, err);
8330 }
8331 }
8332
Eric Andersenc470f442003-07-28 09:56:35 +00008333 switch (n->type) {
8334 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008335#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008336 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008337 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008338 break;
8339#endif
8340 case NNOT:
8341 evaltree(n->nnot.com, EV_TESTED);
8342 status = !exitstatus;
8343 goto setstatus;
8344 case NREDIR:
8345 expredir(n->nredir.redirect);
8346 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8347 if (!status) {
8348 evaltree(n->nredir.n, flags & EV_TESTED);
8349 status = exitstatus;
8350 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008351 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008352 goto setstatus;
8353 case NCMD:
8354 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008355 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008356 if (eflag && !(flags & EV_TESTED))
8357 checkexit = ~0;
8358 goto calleval;
8359 case NFOR:
8360 evalfn = evalfor;
8361 goto calleval;
8362 case NWHILE:
8363 case NUNTIL:
8364 evalfn = evalloop;
8365 goto calleval;
8366 case NSUBSHELL:
8367 case NBACKGND:
8368 evalfn = evalsubshell;
8369 goto calleval;
8370 case NPIPE:
8371 evalfn = evalpipe;
8372 goto checkexit;
8373 case NCASE:
8374 evalfn = evalcase;
8375 goto calleval;
8376 case NAND:
8377 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008378 case NSEMI: {
8379
Eric Andersenc470f442003-07-28 09:56:35 +00008380#if NAND + 1 != NOR
8381#error NAND + 1 != NOR
8382#endif
8383#if NOR + 1 != NSEMI
8384#error NOR + 1 != NSEMI
8385#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008386 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008387 evaltree(
8388 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008389 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008390 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008391 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008392 break;
8393 if (!evalskip) {
8394 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008395 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008396 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008397 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008398 evalfn(n, flags);
8399 break;
8400 }
8401 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008402 }
Eric Andersenc470f442003-07-28 09:56:35 +00008403 case NIF:
8404 evaltree(n->nif.test, EV_TESTED);
8405 if (evalskip)
8406 break;
8407 if (exitstatus == 0) {
8408 n = n->nif.ifpart;
8409 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008410 }
8411 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008412 n = n->nif.elsepart;
8413 goto evaln;
8414 }
8415 goto success;
8416 case NDEFUN:
8417 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008418 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008419 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008420 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008421 exitstatus = status;
8422 break;
8423 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008424
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008425 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008426 exception_handler = savehandler;
8427 out1:
8428 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008429 evalskip |= SKIPEVAL;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008430 else if (pending_sig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008431 goto exexit;
8432
8433 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008434 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008435 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008436 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008437
8438 RESTORE_INT(int_level);
8439 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008440}
8441
Eric Andersenc470f442003-07-28 09:56:35 +00008442#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8443static
8444#endif
8445void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8446
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008447static void
Eric Andersenc470f442003-07-28 09:56:35 +00008448evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008449{
8450 int status;
8451
8452 loopnest++;
8453 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008454 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008455 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008456 int i;
8457
Eric Andersencb57d552001-06-28 07:25:16 +00008458 evaltree(n->nbinary.ch1, EV_TESTED);
8459 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008460 skipping:
8461 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008462 evalskip = 0;
8463 continue;
8464 }
8465 if (evalskip == SKIPBREAK && --skipcount <= 0)
8466 evalskip = 0;
8467 break;
8468 }
Eric Andersenc470f442003-07-28 09:56:35 +00008469 i = exitstatus;
8470 if (n->type != NWHILE)
8471 i = !i;
8472 if (i != 0)
8473 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008474 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008475 status = exitstatus;
8476 if (evalskip)
8477 goto skipping;
8478 }
8479 loopnest--;
8480 exitstatus = status;
8481}
8482
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008483static void
Eric Andersenc470f442003-07-28 09:56:35 +00008484evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008485{
8486 struct arglist arglist;
8487 union node *argp;
8488 struct strlist *sp;
8489 struct stackmark smark;
8490
8491 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008492 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008493 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008494 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008495 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008496 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008497 if (evalskip)
8498 goto out;
8499 }
8500 *arglist.lastp = NULL;
8501
8502 exitstatus = 0;
8503 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008504 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008505 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008506 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008507 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008508 if (evalskip) {
8509 if (evalskip == SKIPCONT && --skipcount <= 0) {
8510 evalskip = 0;
8511 continue;
8512 }
8513 if (evalskip == SKIPBREAK && --skipcount <= 0)
8514 evalskip = 0;
8515 break;
8516 }
8517 }
8518 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008519 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008520 popstackmark(&smark);
8521}
8522
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008523static void
Eric Andersenc470f442003-07-28 09:56:35 +00008524evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008525{
8526 union node *cp;
8527 union node *patp;
8528 struct arglist arglist;
8529 struct stackmark smark;
8530
8531 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008532 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008533 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008534 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008535 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008536 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8537 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008538 if (casematch(patp, arglist.list->text)) {
8539 if (evalskip == 0) {
8540 evaltree(cp->nclist.body, flags);
8541 }
8542 goto out;
8543 }
8544 }
8545 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008546 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008547 popstackmark(&smark);
8548}
8549
Eric Andersenc470f442003-07-28 09:56:35 +00008550/*
8551 * Kick off a subshell to evaluate a tree.
8552 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008553static void
Eric Andersenc470f442003-07-28 09:56:35 +00008554evalsubshell(union node *n, int flags)
8555{
8556 struct job *jp;
8557 int backgnd = (n->type == NBACKGND);
8558 int status;
8559
8560 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008561 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008562 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008563 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008564 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008565 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008566 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008567 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008568 flags |= EV_EXIT;
8569 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008570 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008571 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008572 redirect(n->nredir.redirect, 0);
8573 evaltreenr(n->nredir.n, flags);
8574 /* never returns */
8575 }
8576 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008577 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008578 status = waitforjob(jp);
8579 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008580 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008581}
8582
Eric Andersenc470f442003-07-28 09:56:35 +00008583/*
8584 * Compute the names of the files in a redirection list.
8585 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008586static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008587static void
8588expredir(union node *n)
8589{
8590 union node *redir;
8591
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008592 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008593 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008594
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008595 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008596 fn.lastp = &fn.list;
8597 switch (redir->type) {
8598 case NFROMTO:
8599 case NFROM:
8600 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008601#if ENABLE_ASH_BASH_COMPAT
8602 case NTO2:
8603#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008604 case NCLOBBER:
8605 case NAPPEND:
8606 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008607#if ENABLE_ASH_BASH_COMPAT
8608 store_expfname:
8609#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008610 redir->nfile.expfname = fn.list->text;
8611 break;
8612 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008613 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008614 if (redir->ndup.vname) {
8615 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008616 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008617 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008618#if ENABLE_ASH_BASH_COMPAT
8619//FIXME: we used expandarg with different args!
8620 if (!isdigit_str9(fn.list->text)) {
8621 /* >&file, not >&fd */
8622 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8623 ash_msg_and_raise_error("redir error");
8624 redir->type = NTO2;
8625 goto store_expfname;
8626 }
8627#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008628 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008629 }
8630 break;
8631 }
8632 }
8633}
8634
Eric Andersencb57d552001-06-28 07:25:16 +00008635/*
Eric Andersencb57d552001-06-28 07:25:16 +00008636 * Evaluate a pipeline. All the processes in the pipeline are children
8637 * of the process creating the pipeline. (This differs from some versions
8638 * of the shell, which make the last process in a pipeline the parent
8639 * of all the rest.)
8640 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008641static void
Eric Andersenc470f442003-07-28 09:56:35 +00008642evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008643{
8644 struct job *jp;
8645 struct nodelist *lp;
8646 int pipelen;
8647 int prevfd;
8648 int pip[2];
8649
Eric Andersenc470f442003-07-28 09:56:35 +00008650 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008651 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008652 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008653 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008654 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008655 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008656 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008657 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008658 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008659 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008660 pip[1] = -1;
8661 if (lp->next) {
8662 if (pipe(pip) < 0) {
8663 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008664 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008665 }
8666 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008667 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008668 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008669 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008670 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008671 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008672 if (prevfd > 0) {
8673 dup2(prevfd, 0);
8674 close(prevfd);
8675 }
8676 if (pip[1] > 1) {
8677 dup2(pip[1], 1);
8678 close(pip[1]);
8679 }
Eric Andersenc470f442003-07-28 09:56:35 +00008680 evaltreenr(lp->n, flags);
8681 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008682 }
8683 if (prevfd >= 0)
8684 close(prevfd);
8685 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008686 /* Don't want to trigger debugging */
8687 if (pip[1] != -1)
8688 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008689 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008690 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008691 exitstatus = waitforjob(jp);
8692 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008693 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008694 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008695}
8696
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008697/*
8698 * Controls whether the shell is interactive or not.
8699 */
8700static void
8701setinteractive(int on)
8702{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008703 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008704
8705 if (++on == is_interactive)
8706 return;
8707 is_interactive = on;
8708 setsignal(SIGINT);
8709 setsignal(SIGQUIT);
8710 setsignal(SIGTERM);
8711#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8712 if (is_interactive > 1) {
8713 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008714 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008715
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008716 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008717 /* note: ash and hush share this string */
8718 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008719 "Enter 'help' for a list of built-in commands."
8720 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008721 bb_banner,
8722 "built-in shell (ash)"
8723 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008724 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008725 }
8726 }
8727#endif
8728}
8729
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008730static void
8731optschanged(void)
8732{
8733#if DEBUG
8734 opentrace();
8735#endif
8736 setinteractive(iflag);
8737 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008738#if ENABLE_FEATURE_EDITING_VI
8739 if (viflag)
8740 line_input_state->flags |= VI_MODE;
8741 else
8742 line_input_state->flags &= ~VI_MODE;
8743#else
8744 viflag = 0; /* forcibly keep the option off */
8745#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008746}
8747
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008748static struct localvar *localvars;
8749
8750/*
8751 * Called after a function returns.
8752 * Interrupts must be off.
8753 */
8754static void
8755poplocalvars(void)
8756{
8757 struct localvar *lvp;
8758 struct var *vp;
8759
8760 while ((lvp = localvars) != NULL) {
8761 localvars = lvp->next;
8762 vp = lvp->vp;
Denys Vlasenko883cea42009-07-11 15:31:59 +02008763 TRACE(("poplocalvar %s\n", vp ? vp->text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008764 if (vp == NULL) { /* $- saved */
8765 memcpy(optlist, lvp->text, sizeof(optlist));
8766 free((char*)lvp->text);
8767 optschanged();
8768 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008769 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008770 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008771 if (vp->var_func)
8772 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008773 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008774 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008775 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008776 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008777 }
8778 free(lvp);
8779 }
8780}
8781
8782static int
8783evalfun(struct funcnode *func, int argc, char **argv, int flags)
8784{
8785 volatile struct shparam saveparam;
8786 struct localvar *volatile savelocalvars;
8787 struct jmploc *volatile savehandler;
8788 struct jmploc jmploc;
8789 int e;
8790
8791 saveparam = shellparam;
8792 savelocalvars = localvars;
8793 e = setjmp(jmploc.loc);
8794 if (e) {
8795 goto funcdone;
8796 }
8797 INT_OFF;
8798 savehandler = exception_handler;
8799 exception_handler = &jmploc;
8800 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008801 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008802 func->count++;
8803 funcnest++;
8804 INT_ON;
8805 shellparam.nparam = argc - 1;
8806 shellparam.p = argv + 1;
8807#if ENABLE_ASH_GETOPTS
8808 shellparam.optind = 1;
8809 shellparam.optoff = -1;
8810#endif
8811 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008812 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008813 INT_OFF;
8814 funcnest--;
8815 freefunc(func);
8816 poplocalvars();
8817 localvars = savelocalvars;
8818 freeparam(&shellparam);
8819 shellparam = saveparam;
8820 exception_handler = savehandler;
8821 INT_ON;
8822 evalskip &= ~SKIPFUNC;
8823 return e;
8824}
8825
Denis Vlasenko131ae172007-02-18 13:00:19 +00008826#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008827static char **
8828parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008829{
8830 char *cp, c;
8831
8832 for (;;) {
8833 cp = *++argv;
8834 if (!cp)
8835 return 0;
8836 if (*cp++ != '-')
8837 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008838 c = *cp++;
8839 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008840 break;
8841 if (c == '-' && !*cp) {
8842 argv++;
8843 break;
8844 }
8845 do {
8846 switch (c) {
8847 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008848 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008849 break;
8850 default:
8851 /* run 'typecmd' for other options */
8852 return 0;
8853 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008854 c = *cp++;
8855 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008856 }
8857 return argv;
8858}
8859#endif
8860
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008861/*
8862 * Make a variable a local variable. When a variable is made local, it's
8863 * value and flags are saved in a localvar structure. The saved values
8864 * will be restored when the shell function returns. We handle the name
8865 * "-" as a special case.
8866 */
8867static void
8868mklocal(char *name)
8869{
8870 struct localvar *lvp;
8871 struct var **vpp;
8872 struct var *vp;
8873
8874 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008875 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008876 if (LONE_DASH(name)) {
8877 char *p;
8878 p = ckmalloc(sizeof(optlist));
8879 lvp->text = memcpy(p, optlist, sizeof(optlist));
8880 vp = NULL;
8881 } else {
8882 char *eq;
8883
8884 vpp = hashvar(name);
8885 vp = *findvar(vpp, name);
8886 eq = strchr(name, '=');
8887 if (vp == NULL) {
8888 if (eq)
8889 setvareq(name, VSTRFIXED);
8890 else
8891 setvar(name, NULL, VSTRFIXED);
8892 vp = *vpp; /* the new variable */
8893 lvp->flags = VUNSET;
8894 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008895 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008896 lvp->flags = vp->flags;
8897 vp->flags |= VSTRFIXED|VTEXTFIXED;
8898 if (eq)
8899 setvareq(name, 0);
8900 }
8901 }
8902 lvp->vp = vp;
8903 lvp->next = localvars;
8904 localvars = lvp;
8905 INT_ON;
8906}
8907
8908/*
8909 * The "local" command.
8910 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008911static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008912localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008913{
8914 char *name;
8915
8916 argv = argptr;
8917 while ((name = *argv++) != NULL) {
8918 mklocal(name);
8919 }
8920 return 0;
8921}
8922
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008923static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008924falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008925{
8926 return 1;
8927}
8928
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008929static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008930truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008931{
8932 return 0;
8933}
8934
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008935static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008936execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008937{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008938 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008939 iflag = 0; /* exit on error */
8940 mflag = 0;
8941 optschanged();
8942 shellexec(argv + 1, pathval(), 0);
8943 }
8944 return 0;
8945}
8946
8947/*
8948 * The return command.
8949 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008950static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008951returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008952{
8953 /*
8954 * If called outside a function, do what ksh does;
8955 * skip the rest of the file.
8956 */
8957 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8958 return argv[1] ? number(argv[1]) : exitstatus;
8959}
8960
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008961/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008962static int breakcmd(int, char **) FAST_FUNC;
8963static int dotcmd(int, char **) FAST_FUNC;
8964static int evalcmd(int, char **) FAST_FUNC;
8965static int exitcmd(int, char **) FAST_FUNC;
8966static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008967#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008968static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008969#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008970#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008971static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00008972#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00008973#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008974static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008975#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008976static int readcmd(int, char **) FAST_FUNC;
8977static int setcmd(int, char **) FAST_FUNC;
8978static int shiftcmd(int, char **) FAST_FUNC;
8979static int timescmd(int, char **) FAST_FUNC;
8980static int trapcmd(int, char **) FAST_FUNC;
8981static int umaskcmd(int, char **) FAST_FUNC;
8982static int unsetcmd(int, char **) FAST_FUNC;
8983static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008984
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008985#define BUILTIN_NOSPEC "0"
8986#define BUILTIN_SPECIAL "1"
8987#define BUILTIN_REGULAR "2"
8988#define BUILTIN_SPEC_REG "3"
8989#define BUILTIN_ASSIGN "4"
8990#define BUILTIN_SPEC_ASSG "5"
8991#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008992#define BUILTIN_SPEC_REG_ASSG "7"
8993
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008994/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008995#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008996static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008997#endif
8998#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008999static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009000#endif
9001#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009002static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009003#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009004
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009005/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009006static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009007 { BUILTIN_SPEC_REG "." , dotcmd },
9008 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009009#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009010 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009011#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009012 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009013#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009014#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009015#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009016 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009017#endif
9018#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009019 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009020#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009021 { BUILTIN_SPEC_REG "break" , breakcmd },
9022 { BUILTIN_REGULAR "cd" , cdcmd },
9023 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009024#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009025 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009026#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009027 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009028#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009029 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009030#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009031 { BUILTIN_SPEC_REG "eval" , evalcmd },
9032 { BUILTIN_SPEC_REG "exec" , execcmd },
9033 { BUILTIN_SPEC_REG "exit" , exitcmd },
9034 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9035 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009036#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009037 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009038#endif
9039#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009040 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009041#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009042 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009043#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009044 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009045#endif
9046#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009047 { BUILTIN_REGULAR "jobs" , jobscmd },
9048 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009049#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009050#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009051 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009052#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009053 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009054#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009055 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009056#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009057 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9058 { BUILTIN_REGULAR "read" , readcmd },
9059 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9060 { BUILTIN_SPEC_REG "return" , returncmd },
9061 { BUILTIN_SPEC_REG "set" , setcmd },
9062 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009063#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009064 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009065#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009066#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009067 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009068#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009069 { BUILTIN_SPEC_REG "times" , timescmd },
9070 { BUILTIN_SPEC_REG "trap" , trapcmd },
9071 { BUILTIN_REGULAR "true" , truecmd },
9072 { BUILTIN_NOSPEC "type" , typecmd },
9073 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9074 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009075#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009076 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009077#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009078 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9079 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009080};
9081
Denis Vlasenko80591b02008-03-25 07:49:43 +00009082/* Should match the above table! */
9083#define COMMANDCMD (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#define EXECCMD (builtintab + \
9091 2 + \
9092 1 * ENABLE_ASH_BUILTIN_TEST + \
9093 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9094 1 * ENABLE_ASH_ALIAS + \
9095 1 * ENABLE_ASH_JOB_CONTROL + \
9096 3 + \
9097 1 * ENABLE_ASH_CMDCMD + \
9098 1 + \
9099 ENABLE_ASH_BUILTIN_ECHO + \
9100 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009101
9102/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009103 * Search the table of builtin commands.
9104 */
9105static struct builtincmd *
9106find_builtin(const char *name)
9107{
9108 struct builtincmd *bp;
9109
9110 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009111 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009112 pstrcmp
9113 );
9114 return bp;
9115}
9116
9117/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009118 * Execute a simple command.
9119 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009120static int
9121isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009122{
9123 const char *q = endofname(p);
9124 if (p == q)
9125 return 0;
9126 return *q == '=';
9127}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009128static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009129bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009130{
9131 /* Preserve exitstatus of a previous possible redirection
9132 * as POSIX mandates */
9133 return back_exitstatus;
9134}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009135static void
Eric Andersenc470f442003-07-28 09:56:35 +00009136evalcommand(union node *cmd, int flags)
9137{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009138 static const struct builtincmd null_bltin = {
9139 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009140 };
Eric Andersenc470f442003-07-28 09:56:35 +00009141 struct stackmark smark;
9142 union node *argp;
9143 struct arglist arglist;
9144 struct arglist varlist;
9145 char **argv;
9146 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009147 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009148 struct cmdentry cmdentry;
9149 struct job *jp;
9150 char *lastarg;
9151 const char *path;
9152 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009153 int status;
9154 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009155 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009156 smallint cmd_is_exec;
9157 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009158
9159 /* First expand the arguments. */
9160 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9161 setstackmark(&smark);
9162 back_exitstatus = 0;
9163
9164 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009165 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009166 varlist.lastp = &varlist.list;
9167 *varlist.lastp = NULL;
9168 arglist.lastp = &arglist.list;
9169 *arglist.lastp = NULL;
9170
9171 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009172 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009173 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9174 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9175 }
9176
Eric Andersenc470f442003-07-28 09:56:35 +00009177 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9178 struct strlist **spp;
9179
9180 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009181 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009182 expandarg(argp, &arglist, EXP_VARTILDE);
9183 else
9184 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9185
Eric Andersenc470f442003-07-28 09:56:35 +00009186 for (sp = *spp; sp; sp = sp->next)
9187 argc++;
9188 }
9189
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009190 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009191 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009192 TRACE(("evalcommand arg: %s\n", sp->text));
9193 *nargv++ = sp->text;
9194 }
9195 *nargv = NULL;
9196
9197 lastarg = NULL;
9198 if (iflag && funcnest == 0 && argc > 0)
9199 lastarg = nargv[-1];
9200
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009201 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009202 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009203 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009204
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009205 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009206 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9207 struct strlist **spp;
9208 char *p;
9209
9210 spp = varlist.lastp;
9211 expandarg(argp, &varlist, EXP_VARTILDE);
9212
9213 /*
9214 * Modify the command lookup path, if a PATH= assignment
9215 * is present
9216 */
9217 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009218 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009219 path = p;
9220 }
9221
9222 /* Print the command if xflag is set. */
9223 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009224 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009225 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009226
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009227 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009228 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009229 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009230 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009231 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009232 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009233 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009234 }
9235 sp = arglist.list;
9236 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009237 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009238 }
9239
9240 cmd_is_exec = 0;
9241 spclbltin = -1;
9242
9243 /* Now locate the command. */
9244 if (argc) {
9245 const char *oldpath;
9246 int cmd_flag = DO_ERR;
9247
9248 path += 5;
9249 oldpath = path;
9250 for (;;) {
9251 find_command(argv[0], &cmdentry, cmd_flag, path);
9252 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009253 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009254 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009255 goto bail;
9256 }
9257
9258 /* implement bltin and command here */
9259 if (cmdentry.cmdtype != CMDBUILTIN)
9260 break;
9261 if (spclbltin < 0)
9262 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9263 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009264 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009265#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009266 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009267 path = oldpath;
9268 nargv = parse_command_args(argv, &path);
9269 if (!nargv)
9270 break;
9271 argc -= nargv - argv;
9272 argv = nargv;
9273 cmd_flag |= DO_NOFUNC;
9274 } else
9275#endif
9276 break;
9277 }
9278 }
9279
9280 if (status) {
9281 /* We have a redirection error. */
9282 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009283 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009284 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009285 exitstatus = status;
9286 goto out;
9287 }
9288
9289 /* Execute the command. */
9290 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009291 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009292
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009293#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009294/* (1) BUG: if variables are set, we need to fork, or save/restore them
9295 * around run_nofork_applet() call.
9296 * (2) Should this check also be done in forkshell()?
9297 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9298 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009299 /* find_command() encodes applet_no as (-2 - applet_no) */
9300 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009301 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009302 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009303 /* run <applet>_main() */
9304 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009305 break;
9306 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009307#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009308 /* Can we avoid forking off? For example, very last command
9309 * in a script or a subshell does not need forking,
9310 * we can just exec it.
9311 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009312 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009313 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009314 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009315 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009316 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009317 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009318 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009319 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009320 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009321 break;
9322 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009323 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009324 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009325 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009326 }
9327 listsetvar(varlist.list, VEXPORT|VSTACK);
9328 shellexec(argv, path, cmdentry.u.index);
9329 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009330 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009331 case CMDBUILTIN:
9332 cmdenviron = varlist.list;
9333 if (cmdenviron) {
9334 struct strlist *list = cmdenviron;
9335 int i = VNOSET;
9336 if (spclbltin > 0 || argc == 0) {
9337 i = 0;
9338 if (cmd_is_exec && argc > 1)
9339 i = VEXPORT;
9340 }
9341 listsetvar(list, i);
9342 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009343 /* Tight loop with builtins only:
9344 * "while kill -0 $child; do true; done"
9345 * will never exit even if $child died, unless we do this
9346 * to reap the zombie and make kill detect that it's gone: */
9347 dowait(DOWAIT_NONBLOCK, NULL);
9348
Eric Andersenc470f442003-07-28 09:56:35 +00009349 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9350 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009351 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009352 if (i == EXEXIT)
9353 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009354 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009355 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009356 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009357 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009358 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009359 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009360 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009361 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009362 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009363 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009364 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009365 }
9366 break;
9367
9368 case CMDFUNCTION:
9369 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009370 /* See above for the rationale */
9371 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009372 if (evalfun(cmdentry.u.func, argc, argv, flags))
9373 goto raise;
9374 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009375
9376 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009377
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009378 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009379 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009380 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009381 /* dsl: I think this is intended to be used to support
9382 * '_' in 'vi' command mode during line editing...
9383 * However I implemented that within libedit itself.
9384 */
9385 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009386 }
Eric Andersenc470f442003-07-28 09:56:35 +00009387 popstackmark(&smark);
9388}
9389
9390static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009391evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9392{
Eric Andersenc470f442003-07-28 09:56:35 +00009393 char *volatile savecmdname;
9394 struct jmploc *volatile savehandler;
9395 struct jmploc jmploc;
9396 int i;
9397
9398 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009399 i = setjmp(jmploc.loc);
9400 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009401 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009402 savehandler = exception_handler;
9403 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009404 commandname = argv[0];
9405 argptr = argv + 1;
9406 optptr = NULL; /* initialize nextopt */
9407 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009408 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009409 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009410 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009411 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009412 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009413 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009414
9415 return i;
9416}
9417
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009418static int
9419goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009420{
9421 return !*endofname(p);
9422}
9423
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009424
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009425/*
9426 * Search for a command. This is called before we fork so that the
9427 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009428 * the child. The check for "goodname" is an overly conservative
9429 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009430 */
Eric Andersenc470f442003-07-28 09:56:35 +00009431static void
9432prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009433{
9434 struct cmdentry entry;
9435
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009436 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9437 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009438}
9439
Eric Andersencb57d552001-06-28 07:25:16 +00009440
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009441/* ============ Builtin commands
9442 *
9443 * Builtin commands whose functions are closely tied to evaluation
9444 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009445 */
9446
9447/*
Eric Andersencb57d552001-06-28 07:25:16 +00009448 * Handle break and continue commands. Break, continue, and return are
9449 * all handled by setting the evalskip flag. The evaluation routines
9450 * above all check this flag, and if it is set they start skipping
9451 * commands rather than executing them. The variable skipcount is
9452 * the number of loops to break/continue, or the number of function
9453 * levels to return. (The latter is always 1.) It should probably
9454 * be an error to break out of more loops than exist, but it isn't
9455 * in the standard shell so we don't make it one here.
9456 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009457static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009458breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009459{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009460 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009461
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009462 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009463 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009464 if (n > loopnest)
9465 n = loopnest;
9466 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009467 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009468 skipcount = n;
9469 }
9470 return 0;
9471}
9472
Eric Andersenc470f442003-07-28 09:56:35 +00009473
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009474/* ============ input.c
9475 *
Eric Andersen90898442003-08-06 11:20:52 +00009476 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009477 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009478
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009479enum {
9480 INPUT_PUSH_FILE = 1,
9481 INPUT_NOFILE_OK = 2,
9482};
Eric Andersencb57d552001-06-28 07:25:16 +00009483
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009484static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009485/* values of checkkwd variable */
9486#define CHKALIAS 0x1
9487#define CHKKWD 0x2
9488#define CHKNL 0x4
9489
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009490/*
9491 * Push a string back onto the input at this current parsefile level.
9492 * We handle aliases this way.
9493 */
9494#if !ENABLE_ASH_ALIAS
9495#define pushstring(s, ap) pushstring(s)
9496#endif
9497static void
9498pushstring(char *s, struct alias *ap)
9499{
9500 struct strpush *sp;
9501 int len;
9502
9503 len = strlen(s);
9504 INT_OFF;
9505 if (g_parsefile->strpush) {
9506 sp = ckzalloc(sizeof(*sp));
9507 sp->prev = g_parsefile->strpush;
9508 } else {
9509 sp = &(g_parsefile->basestrpush);
9510 }
9511 g_parsefile->strpush = sp;
9512 sp->prev_string = g_parsefile->next_to_pgetc;
9513 sp->prev_left_in_line = g_parsefile->left_in_line;
9514#if ENABLE_ASH_ALIAS
9515 sp->ap = ap;
9516 if (ap) {
9517 ap->flag |= ALIASINUSE;
9518 sp->string = s;
9519 }
9520#endif
9521 g_parsefile->next_to_pgetc = s;
9522 g_parsefile->left_in_line = len;
9523 INT_ON;
9524}
9525
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009526static void
9527popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009528{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009529 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009530
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009531 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009532#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009533 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009534 if (g_parsefile->next_to_pgetc[-1] == ' '
9535 || g_parsefile->next_to_pgetc[-1] == '\t'
9536 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009537 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009538 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009539 if (sp->string != sp->ap->val) {
9540 free(sp->string);
9541 }
9542 sp->ap->flag &= ~ALIASINUSE;
9543 if (sp->ap->flag & ALIASDEAD) {
9544 unalias(sp->ap->name);
9545 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009546 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009547#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009548 g_parsefile->next_to_pgetc = sp->prev_string;
9549 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009550 g_parsefile->strpush = sp->prev;
9551 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009552 free(sp);
9553 INT_ON;
9554}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009555
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009556//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9557//it peeks whether it is &>, and then pushes back both chars.
9558//This function needs to save last *next_to_pgetc to buf[0]
9559//to make two pungetc() reliable. Currently,
9560// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009561static int
9562preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009563{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009564 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009565 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009566
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009567 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009568#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009569 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009570 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9571 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009572 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009573#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009574 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009575#endif
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02009576 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009577 if (nr == 0) {
9578 /* Ctrl+C pressed */
9579 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009580 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009581 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009582 raise(SIGINT);
9583 return 1;
9584 }
Eric Andersenc470f442003-07-28 09:56:35 +00009585 goto retry;
9586 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009587 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009588 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009589 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009590 }
Eric Andersencb57d552001-06-28 07:25:16 +00009591 }
9592#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009593 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009594#endif
9595
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009596#if 0
9597/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009598 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009599 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009600 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009601 if (flags >= 0 && (flags & O_NONBLOCK)) {
9602 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009603 if (fcntl(0, F_SETFL, flags) >= 0) {
9604 out2str("sh: turning off NDELAY mode\n");
9605 goto retry;
9606 }
9607 }
9608 }
9609 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009610#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009611 return nr;
9612}
9613
9614/*
9615 * Refill the input buffer and return the next input character:
9616 *
9617 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009618 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9619 * or we are reading from a string so we can't refill the buffer,
9620 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009621 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009622 * 4) Process input up to the next newline, deleting nul characters.
9623 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009624//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9625#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009626static int
Eric Andersenc470f442003-07-28 09:56:35 +00009627preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009628{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009629 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009630 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009631
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009632 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009633#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009634 if (g_parsefile->left_in_line == -1
9635 && g_parsefile->strpush->ap
9636 && g_parsefile->next_to_pgetc[-1] != ' '
9637 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009638 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009639 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009640 return PEOA;
9641 }
Eric Andersen2870d962001-07-02 17:27:21 +00009642#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009643 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009644 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009645 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9646 g_parsefile->left_in_line,
9647 g_parsefile->next_to_pgetc,
9648 g_parsefile->next_to_pgetc);
9649 if (--g_parsefile->left_in_line >= 0)
9650 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009651 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009652 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009653 * "pgetc" needs refilling.
9654 */
9655
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009656 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009657 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009658 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009659 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009660 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009661 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009662 /* even in failure keep left_in_line and next_to_pgetc
9663 * in lock step, for correct multi-layer pungetc.
9664 * left_in_line was decremented before preadbuffer(),
9665 * must inc next_to_pgetc: */
9666 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009667 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009668 }
Eric Andersencb57d552001-06-28 07:25:16 +00009669
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009670 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009671 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009672 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009673 again:
9674 more = preadfd();
9675 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009676 /* don't try reading again */
9677 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009678 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009679 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009680 return PEOF;
9681 }
9682 }
9683
Denis Vlasenko727752d2008-11-28 03:41:47 +00009684 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009685 * Set g_parsefile->left_in_line
9686 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009687 * NUL chars are deleted.
9688 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009689 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009690 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009691 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009692
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009693 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009694
Denis Vlasenko727752d2008-11-28 03:41:47 +00009695 c = *q;
9696 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009697 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009698 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009699 q++;
9700 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009701 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009702 break;
9703 }
Eric Andersencb57d552001-06-28 07:25:16 +00009704 }
9705
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009706 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009707 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9708 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009709 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009710 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009711 }
9712 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009713 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009714
Eric Andersencb57d552001-06-28 07:25:16 +00009715 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009716 char save = *q;
9717 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009718 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009719 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009720 }
9721
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009722 pgetc_debug("preadbuffer at %d:%p'%s'",
9723 g_parsefile->left_in_line,
9724 g_parsefile->next_to_pgetc,
9725 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009726 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009727}
9728
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009729#define pgetc_as_macro() \
9730 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009731 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009732 : preadbuffer() \
9733 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009734
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009735static int
9736pgetc(void)
9737{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009738 pgetc_debug("pgetc_fast at %d:%p'%s'",
9739 g_parsefile->left_in_line,
9740 g_parsefile->next_to_pgetc,
9741 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009742 return pgetc_as_macro();
9743}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009744
9745#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009746# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009747#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009748# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009749#endif
9750
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009751#if ENABLE_ASH_ALIAS
9752static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009753pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009754{
9755 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009756 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009757 pgetc_debug("pgetc_fast at %d:%p'%s'",
9758 g_parsefile->left_in_line,
9759 g_parsefile->next_to_pgetc,
9760 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009761 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009762 } while (c == PEOA);
9763 return c;
9764}
9765#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009766# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009767#endif
9768
9769/*
9770 * Read a line from the script.
9771 */
9772static char *
9773pfgets(char *line, int len)
9774{
9775 char *p = line;
9776 int nleft = len;
9777 int c;
9778
9779 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009780 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009781 if (c == PEOF) {
9782 if (p == line)
9783 return NULL;
9784 break;
9785 }
9786 *p++ = c;
9787 if (c == '\n')
9788 break;
9789 }
9790 *p = '\0';
9791 return line;
9792}
9793
Eric Andersenc470f442003-07-28 09:56:35 +00009794/*
9795 * Undo the last call to pgetc. Only one character may be pushed back.
9796 * PEOF may be pushed back.
9797 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009798static void
Eric Andersenc470f442003-07-28 09:56:35 +00009799pungetc(void)
9800{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009801 g_parsefile->left_in_line++;
9802 g_parsefile->next_to_pgetc--;
9803 pgetc_debug("pushed back to %d:%p'%s'",
9804 g_parsefile->left_in_line,
9805 g_parsefile->next_to_pgetc,
9806 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009807}
9808
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009809/*
9810 * To handle the "." command, a stack of input files is used. Pushfile
9811 * adds a new entry to the stack and popfile restores the previous level.
9812 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009813static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009814pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009815{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009816 struct parsefile *pf;
9817
Denis Vlasenko597906c2008-02-20 16:38:54 +00009818 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009819 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009820 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009821 /*pf->strpush = NULL; - ckzalloc did it */
9822 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009823 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009824}
9825
9826static void
9827popfile(void)
9828{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009829 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009830
Denis Vlasenkob012b102007-02-19 22:43:01 +00009831 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009832 if (pf->pf_fd >= 0)
9833 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009834 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009835 while (pf->strpush)
9836 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009837 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009838 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009839 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009840}
9841
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009842/*
9843 * Return to top level.
9844 */
9845static void
9846popallfiles(void)
9847{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009848 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009849 popfile();
9850}
9851
9852/*
9853 * Close the file(s) that the shell is reading commands from. Called
9854 * after a fork is done.
9855 */
9856static void
9857closescript(void)
9858{
9859 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009860 if (g_parsefile->pf_fd > 0) {
9861 close(g_parsefile->pf_fd);
9862 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009863 }
9864}
9865
9866/*
9867 * Like setinputfile, but takes an open file descriptor. Call this with
9868 * interrupts off.
9869 */
9870static void
9871setinputfd(int fd, int push)
9872{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009873 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009874 if (push) {
9875 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009876 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009877 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009878 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009879 if (g_parsefile->buf == NULL)
9880 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009881 g_parsefile->left_in_buffer = 0;
9882 g_parsefile->left_in_line = 0;
9883 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009884}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009885
Eric Andersenc470f442003-07-28 09:56:35 +00009886/*
9887 * Set the input to take input from a file. If push is set, push the
9888 * old input onto the stack first.
9889 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009890static int
9891setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009892{
9893 int fd;
9894 int fd2;
9895
Denis Vlasenkob012b102007-02-19 22:43:01 +00009896 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009897 fd = open(fname, O_RDONLY);
9898 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009899 if (flags & INPUT_NOFILE_OK)
9900 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009901 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009902 }
Eric Andersenc470f442003-07-28 09:56:35 +00009903 if (fd < 10) {
9904 fd2 = copyfd(fd, 10);
9905 close(fd);
9906 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009907 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009908 fd = fd2;
9909 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009910 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009911 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009912 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009913 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009914}
9915
Eric Andersencb57d552001-06-28 07:25:16 +00009916/*
9917 * Like setinputfile, but takes input from a string.
9918 */
Eric Andersenc470f442003-07-28 09:56:35 +00009919static void
9920setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009921{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009922 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009923 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009924 g_parsefile->next_to_pgetc = string;
9925 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009926 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009927 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009928 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009929}
9930
9931
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009932/* ============ mail.c
9933 *
9934 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009935 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009936
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009937#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009938
Eric Andersencb57d552001-06-28 07:25:16 +00009939#define MAXMBOXES 10
9940
Eric Andersenc470f442003-07-28 09:56:35 +00009941/* times of mailboxes */
9942static time_t mailtime[MAXMBOXES];
9943/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009944static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009945
Eric Andersencb57d552001-06-28 07:25:16 +00009946/*
Eric Andersenc470f442003-07-28 09:56:35 +00009947 * Print appropriate message(s) if mail has arrived.
9948 * If mail_var_path_changed is set,
9949 * then the value of MAIL has mail_var_path_changed,
9950 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009951 */
Eric Andersenc470f442003-07-28 09:56:35 +00009952static void
9953chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009954{
Eric Andersencb57d552001-06-28 07:25:16 +00009955 const char *mpath;
9956 char *p;
9957 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009958 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009959 struct stackmark smark;
9960 struct stat statb;
9961
Eric Andersencb57d552001-06-28 07:25:16 +00009962 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009963 mpath = mpathset() ? mpathval() : mailval();
9964 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02009965 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00009966 if (p == NULL)
9967 break;
9968 if (*p == '\0')
9969 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009970 for (q = p; *q; q++)
9971 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009972#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009973 if (q[-1] != '/')
9974 abort();
9975#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009976 q[-1] = '\0'; /* delete trailing '/' */
9977 if (stat(p, &statb) < 0) {
9978 *mtp = 0;
9979 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009980 }
Eric Andersenc470f442003-07-28 09:56:35 +00009981 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9982 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02009983 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +00009984 pathopt ? pathopt : "you have mail"
9985 );
9986 }
9987 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009988 }
Eric Andersenc470f442003-07-28 09:56:35 +00009989 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009990 popstackmark(&smark);
9991}
Eric Andersencb57d552001-06-28 07:25:16 +00009992
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009993static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009994changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009995{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009996 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009997}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009998
Denis Vlasenko131ae172007-02-18 13:00:19 +00009999#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010000
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010001
10002/* ============ ??? */
10003
Eric Andersencb57d552001-06-28 07:25:16 +000010004/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010005 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010006 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010007static void
10008setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010009{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010010 char **newparam;
10011 char **ap;
10012 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010013
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010014 for (nparam = 0; argv[nparam]; nparam++)
10015 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010016 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10017 while (*argv) {
10018 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010019 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010020 *ap = NULL;
10021 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010022 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010023 shellparam.nparam = nparam;
10024 shellparam.p = newparam;
10025#if ENABLE_ASH_GETOPTS
10026 shellparam.optind = 1;
10027 shellparam.optoff = -1;
10028#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010029}
10030
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010031/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010032 * Process shell options. The global variable argptr contains a pointer
10033 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010034 *
10035 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10036 * For a non-interactive shell, an error condition encountered
10037 * by a special built-in ... shall cause the shell to write a diagnostic message
10038 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010039 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010040 * ...
10041 * Utility syntax error (option or operand error) Shall exit
10042 * ...
10043 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10044 * we see that bash does not do that (set "finishes" with error code 1 instead,
10045 * and shell continues), and people rely on this behavior!
10046 * Testcase:
10047 * set -o barfoo 2>/dev/null
10048 * echo $?
10049 *
10050 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010051 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010052static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010053plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010054{
10055 int i;
10056
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010057 if (name) {
10058 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010059 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010060 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010061 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010062 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010063 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010064 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010065 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010066 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010067 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010068 if (val) {
10069 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10070 } else {
10071 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10072 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010073 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010074 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010075}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010076static void
10077setoption(int flag, int val)
10078{
10079 int i;
10080
10081 for (i = 0; i < NOPTS; i++) {
10082 if (optletters(i) == flag) {
10083 optlist[i] = val;
10084 return;
10085 }
10086 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010087 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010088 /* NOTREACHED */
10089}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010090static int
Eric Andersenc470f442003-07-28 09:56:35 +000010091options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010092{
10093 char *p;
10094 int val;
10095 int c;
10096
10097 if (cmdline)
10098 minusc = NULL;
10099 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010100 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010101 if (c != '-' && c != '+')
10102 break;
10103 argptr++;
10104 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010105 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010106 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010107 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010108 if (!cmdline) {
10109 /* "-" means turn off -x and -v */
10110 if (p[0] == '\0')
10111 xflag = vflag = 0;
10112 /* "--" means reset params */
10113 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010114 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010115 }
Eric Andersenc470f442003-07-28 09:56:35 +000010116 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010117 }
Eric Andersencb57d552001-06-28 07:25:16 +000010118 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010119 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010120 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010121 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010122 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010123 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010124 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010125 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010126 /* it already printed err message */
10127 return 1; /* error */
10128 }
Eric Andersencb57d552001-06-28 07:25:16 +000010129 if (*argptr)
10130 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010131 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10132 isloginsh = 1;
10133 /* bash does not accept +-login, we also won't */
10134 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010135 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010136 isloginsh = 1;
10137 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010138 } else {
10139 setoption(c, val);
10140 }
10141 }
10142 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010143 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010144}
10145
Eric Andersencb57d552001-06-28 07:25:16 +000010146/*
Eric Andersencb57d552001-06-28 07:25:16 +000010147 * The shift builtin command.
10148 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010149static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010150shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010151{
10152 int n;
10153 char **ap1, **ap2;
10154
10155 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010156 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010157 n = number(argv[1]);
10158 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010159 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010160 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010161 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010162 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010163 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010164 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010165 }
10166 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010167 while ((*ap2++ = *ap1++) != NULL)
10168 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010169#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010170 shellparam.optind = 1;
10171 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010172#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010173 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010174 return 0;
10175}
10176
Eric Andersencb57d552001-06-28 07:25:16 +000010177/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010178 * POSIX requires that 'set' (but not export or readonly) output the
10179 * variables in lexicographic order - by the locale's collating order (sigh).
10180 * Maybe we could keep them in an ordered balanced binary tree
10181 * instead of hashed lists.
10182 * For now just roll 'em through qsort for printing...
10183 */
10184static int
10185showvars(const char *sep_prefix, int on, int off)
10186{
10187 const char *sep;
10188 char **ep, **epend;
10189
10190 ep = listvars(on, off, &epend);
10191 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10192
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010193 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010194
10195 for (; ep < epend; ep++) {
10196 const char *p;
10197 const char *q;
10198
10199 p = strchrnul(*ep, '=');
10200 q = nullstr;
10201 if (*p)
10202 q = single_quote(++p);
10203 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10204 }
10205 return 0;
10206}
10207
10208/*
Eric Andersencb57d552001-06-28 07:25:16 +000010209 * The set command builtin.
10210 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010211static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010212setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010213{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010214 int retval;
10215
Denis Vlasenko68404f12008-03-17 09:00:54 +000010216 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010217 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010218 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010219 retval = 1;
10220 if (!options(0)) { /* if no parse error... */
10221 retval = 0;
10222 optschanged();
10223 if (*argptr != NULL) {
10224 setparam(argptr);
10225 }
Eric Andersencb57d552001-06-28 07:25:16 +000010226 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010227 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010228 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010229}
10230
Denis Vlasenko131ae172007-02-18 13:00:19 +000010231#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010232static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010233change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010234{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010235 uint32_t t;
10236
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010237 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010238 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010239 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010240 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010241 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010242 vrandom.flags &= ~VNOFUNC;
10243 } else {
10244 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010245 t = strtoul(value, NULL, 10);
10246 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010247 }
Eric Andersenef02f822004-03-11 13:34:24 +000010248}
Eric Andersen16767e22004-03-16 05:14:10 +000010249#endif
10250
Denis Vlasenko131ae172007-02-18 13:00:19 +000010251#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010252static int
Eric Andersenc470f442003-07-28 09:56:35 +000010253getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010254{
10255 char *p, *q;
10256 char c = '?';
10257 int done = 0;
10258 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010259 char s[12];
10260 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010261
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010262 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010263 return 1;
10264 optnext = optfirst + *param_optind - 1;
10265
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010266 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010267 p = NULL;
10268 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010269 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010270 if (p == NULL || *p == '\0') {
10271 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010272 p = *optnext;
10273 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010274 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010275 p = NULL;
10276 done = 1;
10277 goto out;
10278 }
10279 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010280 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010281 goto atend;
10282 }
10283
10284 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010285 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010286 if (*q == '\0') {
10287 if (optstr[0] == ':') {
10288 s[0] = c;
10289 s[1] = '\0';
10290 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010291 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010292 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010293 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010294 }
10295 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010296 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010297 }
10298 if (*++q == ':')
10299 q++;
10300 }
10301
10302 if (*++q == ':') {
10303 if (*p == '\0' && (p = *optnext) == NULL) {
10304 if (optstr[0] == ':') {
10305 s[0] = c;
10306 s[1] = '\0';
10307 err |= setvarsafe("OPTARG", s, 0);
10308 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010309 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010310 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010311 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010312 c = '?';
10313 }
Eric Andersenc470f442003-07-28 09:56:35 +000010314 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010315 }
10316
10317 if (p == *optnext)
10318 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010319 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010320 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010321 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010322 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010323 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010324 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010325 *param_optind = optnext - optfirst + 1;
10326 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010327 err |= setvarsafe("OPTIND", s, VNOFUNC);
10328 s[0] = c;
10329 s[1] = '\0';
10330 err |= setvarsafe(optvar, s, 0);
10331 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010332 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010333 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010334 flush_stdout_stderr();
10335 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010336 }
10337 return done;
10338}
Eric Andersenc470f442003-07-28 09:56:35 +000010339
10340/*
10341 * The getopts builtin. Shellparam.optnext points to the next argument
10342 * to be processed. Shellparam.optptr points to the next character to
10343 * be processed in the current argument. If shellparam.optnext is NULL,
10344 * then it's the first time getopts has been called.
10345 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010346static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010347getoptscmd(int argc, char **argv)
10348{
10349 char **optbase;
10350
10351 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010352 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010353 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010354 optbase = shellparam.p;
10355 if (shellparam.optind > shellparam.nparam + 1) {
10356 shellparam.optind = 1;
10357 shellparam.optoff = -1;
10358 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010359 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010360 optbase = &argv[3];
10361 if (shellparam.optind > argc - 2) {
10362 shellparam.optind = 1;
10363 shellparam.optoff = -1;
10364 }
10365 }
10366
10367 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010368 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010369}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010370#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010371
Eric Andersencb57d552001-06-28 07:25:16 +000010372
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010373/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010374
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010375struct heredoc {
10376 struct heredoc *next; /* next here document in list */
10377 union node *here; /* redirection node */
10378 char *eofmark; /* string indicating end of input */
10379 smallint striptabs; /* if set, strip leading tabs */
10380};
10381
10382static smallint tokpushback; /* last token pushed back */
10383static smallint parsebackquote; /* nonzero if we are inside backquotes */
10384static smallint quoteflag; /* set if (part of) last token was quoted */
10385static token_id_t lasttoken; /* last token read (integer id Txxx) */
10386static struct heredoc *heredoclist; /* list of here documents to read */
10387static char *wordtext; /* text of last word returned by readtoken */
10388static struct nodelist *backquotelist;
10389static union node *redirnode;
10390static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010391
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010392static const char *
10393tokname(char *buf, int tok)
10394{
10395 if (tok < TSEMI)
10396 return tokname_array[tok] + 1;
10397 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10398 return buf;
10399}
10400
10401/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010402 * Called when an unexpected token is read during the parse. The argument
10403 * is the token that is expected, or -1 if more than one type of token can
10404 * occur at this point.
10405 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010406static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010407static void
10408raise_error_unexpected_syntax(int token)
10409{
10410 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010411 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010412 int l;
10413
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010414 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010415 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010416 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010417 raise_error_syntax(msg);
10418 /* NOTREACHED */
10419}
Eric Andersencb57d552001-06-28 07:25:16 +000010420
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010421#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010422
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010423/* parsing is heavily cross-recursive, need these forward decls */
10424static union node *andor(void);
10425static union node *pipeline(void);
10426static union node *parse_command(void);
10427static void parseheredoc(void);
10428static char peektoken(void);
10429static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010430
Eric Andersenc470f442003-07-28 09:56:35 +000010431static union node *
10432list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010433{
10434 union node *n1, *n2, *n3;
10435 int tok;
10436
Eric Andersenc470f442003-07-28 09:56:35 +000010437 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10438 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010439 return NULL;
10440 n1 = NULL;
10441 for (;;) {
10442 n2 = andor();
10443 tok = readtoken();
10444 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010445 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010446 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010447 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010448 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010449 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010450 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010451 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010452 n2 = n3;
10453 }
10454 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010455 }
10456 }
10457 if (n1 == NULL) {
10458 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010459 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010460 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010461 n3->type = NSEMI;
10462 n3->nbinary.ch1 = n1;
10463 n3->nbinary.ch2 = n2;
10464 n1 = n3;
10465 }
10466 switch (tok) {
10467 case TBACKGND:
10468 case TSEMI:
10469 tok = readtoken();
10470 /* fall through */
10471 case TNL:
10472 if (tok == TNL) {
10473 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010474 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010475 return n1;
10476 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010477 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010478 }
Eric Andersenc470f442003-07-28 09:56:35 +000010479 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010480 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010481 return n1;
10482 break;
10483 case TEOF:
10484 if (heredoclist)
10485 parseheredoc();
10486 else
Eric Andersenc470f442003-07-28 09:56:35 +000010487 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010488 return n1;
10489 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010490 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010491 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010492 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010493 return n1;
10494 }
10495 }
10496}
10497
Eric Andersenc470f442003-07-28 09:56:35 +000010498static union node *
10499andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010500{
Eric Andersencb57d552001-06-28 07:25:16 +000010501 union node *n1, *n2, *n3;
10502 int t;
10503
Eric Andersencb57d552001-06-28 07:25:16 +000010504 n1 = pipeline();
10505 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010506 t = readtoken();
10507 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010508 t = NAND;
10509 } else if (t == TOR) {
10510 t = NOR;
10511 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010512 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010513 return n1;
10514 }
Eric Andersenc470f442003-07-28 09:56:35 +000010515 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010516 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010517 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010518 n3->type = t;
10519 n3->nbinary.ch1 = n1;
10520 n3->nbinary.ch2 = n2;
10521 n1 = n3;
10522 }
10523}
10524
Eric Andersenc470f442003-07-28 09:56:35 +000010525static union node *
10526pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010527{
Eric Andersencb57d552001-06-28 07:25:16 +000010528 union node *n1, *n2, *pipenode;
10529 struct nodelist *lp, *prev;
10530 int negate;
10531
10532 negate = 0;
10533 TRACE(("pipeline: entered\n"));
10534 if (readtoken() == TNOT) {
10535 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010536 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010537 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010538 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010539 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010540 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010541 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010542 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010543 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010544 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010545 pipenode->npipe.cmdlist = lp;
10546 lp->n = n1;
10547 do {
10548 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010549 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010550 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010551 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010552 prev->next = lp;
10553 } while (readtoken() == TPIPE);
10554 lp->next = NULL;
10555 n1 = pipenode;
10556 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010557 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010558 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010559 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010560 n2->type = NNOT;
10561 n2->nnot.com = n1;
10562 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010563 }
10564 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010565}
10566
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010567static union node *
10568makename(void)
10569{
10570 union node *n;
10571
Denis Vlasenko597906c2008-02-20 16:38:54 +000010572 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010573 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010574 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010575 n->narg.text = wordtext;
10576 n->narg.backquote = backquotelist;
10577 return n;
10578}
10579
10580static void
10581fixredir(union node *n, const char *text, int err)
10582{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010583 int fd;
10584
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010585 TRACE(("Fix redir %s %d\n", text, err));
10586 if (!err)
10587 n->ndup.vname = NULL;
10588
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010589 fd = bb_strtou(text, NULL, 10);
10590 if (!errno && fd >= 0)
10591 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010592 else if (LONE_DASH(text))
10593 n->ndup.dupfd = -1;
10594 else {
10595 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010596 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010597 n->ndup.vname = makename();
10598 }
10599}
10600
10601/*
10602 * Returns true if the text contains nothing to expand (no dollar signs
10603 * or backquotes).
10604 */
10605static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010606noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010607{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010608 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010609
Denys Vlasenkocd716832009-11-28 22:14:02 +010010610 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010611 if (c == CTLQUOTEMARK)
10612 continue;
10613 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010614 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010615 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010616 return 0;
10617 }
10618 return 1;
10619}
10620
10621static void
10622parsefname(void)
10623{
10624 union node *n = redirnode;
10625
10626 if (readtoken() != TWORD)
10627 raise_error_unexpected_syntax(-1);
10628 if (n->type == NHERE) {
10629 struct heredoc *here = heredoc;
10630 struct heredoc *p;
10631 int i;
10632
10633 if (quoteflag == 0)
10634 n->type = NXHERE;
10635 TRACE(("Here document %d\n", n->type));
10636 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010637 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010638 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010639 here->eofmark = wordtext;
10640 here->next = NULL;
10641 if (heredoclist == NULL)
10642 heredoclist = here;
10643 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010644 for (p = heredoclist; p->next; p = p->next)
10645 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010646 p->next = here;
10647 }
10648 } else if (n->type == NTOFD || n->type == NFROMFD) {
10649 fixredir(n, wordtext, 0);
10650 } else {
10651 n->nfile.fname = makename();
10652 }
10653}
Eric Andersencb57d552001-06-28 07:25:16 +000010654
Eric Andersenc470f442003-07-28 09:56:35 +000010655static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010656simplecmd(void)
10657{
10658 union node *args, **app;
10659 union node *n = NULL;
10660 union node *vars, **vpp;
10661 union node **rpp, *redir;
10662 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010663#if ENABLE_ASH_BASH_COMPAT
10664 smallint double_brackets_flag = 0;
10665#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010666
10667 args = NULL;
10668 app = &args;
10669 vars = NULL;
10670 vpp = &vars;
10671 redir = NULL;
10672 rpp = &redir;
10673
10674 savecheckkwd = CHKALIAS;
10675 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010676 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010677 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010678 t = readtoken();
10679 switch (t) {
10680#if ENABLE_ASH_BASH_COMPAT
10681 case TAND: /* "&&" */
10682 case TOR: /* "||" */
10683 if (!double_brackets_flag) {
10684 tokpushback = 1;
10685 goto out;
10686 }
10687 wordtext = (char *) (t == TAND ? "-a" : "-o");
10688#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010689 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010690 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010691 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010692 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010693 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010694#if ENABLE_ASH_BASH_COMPAT
10695 if (strcmp("[[", wordtext) == 0)
10696 double_brackets_flag = 1;
10697 else if (strcmp("]]", wordtext) == 0)
10698 double_brackets_flag = 0;
10699#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010700 n->narg.backquote = backquotelist;
10701 if (savecheckkwd && isassignment(wordtext)) {
10702 *vpp = n;
10703 vpp = &n->narg.next;
10704 } else {
10705 *app = n;
10706 app = &n->narg.next;
10707 savecheckkwd = 0;
10708 }
10709 break;
10710 case TREDIR:
10711 *rpp = n = redirnode;
10712 rpp = &n->nfile.next;
10713 parsefname(); /* read name of redirection file */
10714 break;
10715 case TLP:
10716 if (args && app == &args->narg.next
10717 && !vars && !redir
10718 ) {
10719 struct builtincmd *bcmd;
10720 const char *name;
10721
10722 /* We have a function */
10723 if (readtoken() != TRP)
10724 raise_error_unexpected_syntax(TRP);
10725 name = n->narg.text;
10726 if (!goodname(name)
10727 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10728 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010729 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010730 }
10731 n->type = NDEFUN;
10732 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10733 n->narg.next = parse_command();
10734 return n;
10735 }
10736 /* fall through */
10737 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010738 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010739 goto out;
10740 }
10741 }
10742 out:
10743 *app = NULL;
10744 *vpp = NULL;
10745 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010746 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010747 n->type = NCMD;
10748 n->ncmd.args = args;
10749 n->ncmd.assign = vars;
10750 n->ncmd.redirect = redir;
10751 return n;
10752}
10753
10754static union node *
10755parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010756{
Eric Andersencb57d552001-06-28 07:25:16 +000010757 union node *n1, *n2;
10758 union node *ap, **app;
10759 union node *cp, **cpp;
10760 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010761 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010762 int t;
10763
10764 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010765 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010766
Eric Andersencb57d552001-06-28 07:25:16 +000010767 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010768 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010769 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010770 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010771 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010772 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010773 n1->type = NIF;
10774 n1->nif.test = list(0);
10775 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010776 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010777 n1->nif.ifpart = list(0);
10778 n2 = n1;
10779 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010780 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010781 n2 = n2->nif.elsepart;
10782 n2->type = NIF;
10783 n2->nif.test = list(0);
10784 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010785 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010786 n2->nif.ifpart = list(0);
10787 }
10788 if (lasttoken == TELSE)
10789 n2->nif.elsepart = list(0);
10790 else {
10791 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010792 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010793 }
Eric Andersenc470f442003-07-28 09:56:35 +000010794 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010795 break;
10796 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010797 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010798 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010799 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010800 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010801 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010802 got = readtoken();
10803 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010804 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010805 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010806 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010807 }
10808 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010809 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010810 break;
10811 }
10812 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010813 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010814 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010815 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010816 n1->type = NFOR;
10817 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010818 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010819 if (readtoken() == TIN) {
10820 app = &ap;
10821 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010822 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010823 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010824 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010825 n2->narg.text = wordtext;
10826 n2->narg.backquote = backquotelist;
10827 *app = n2;
10828 app = &n2->narg.next;
10829 }
10830 *app = NULL;
10831 n1->nfor.args = ap;
10832 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010833 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010834 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010835 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010836 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010837 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010838 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010839 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010840 n1->nfor.args = n2;
10841 /*
10842 * Newline or semicolon here is optional (but note
10843 * that the original Bourne shell only allowed NL).
10844 */
10845 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010846 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010847 }
Eric Andersenc470f442003-07-28 09:56:35 +000010848 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010849 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010850 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010851 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010852 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010853 break;
10854 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010855 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010856 n1->type = NCASE;
10857 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010858 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010859 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010860 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010861 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010862 n2->narg.text = wordtext;
10863 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010864 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010865 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010866 } while (readtoken() == TNL);
10867 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010868 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010869 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010870 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010871 checkkwd = CHKNL | CHKKWD;
10872 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010873 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010874 if (lasttoken == TLP)
10875 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010876 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010877 cp->type = NCLIST;
10878 app = &cp->nclist.pattern;
10879 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010880 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010881 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010882 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010883 ap->narg.text = wordtext;
10884 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010885 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010886 break;
10887 app = &ap->narg.next;
10888 readtoken();
10889 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010890 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010891 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010892 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010893 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010894
Eric Andersenc470f442003-07-28 09:56:35 +000010895 cpp = &cp->nclist.next;
10896
10897 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010898 t = readtoken();
10899 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010900 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010901 raise_error_unexpected_syntax(TENDCASE);
10902 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010903 }
Eric Andersenc470f442003-07-28 09:56:35 +000010904 }
Eric Andersencb57d552001-06-28 07:25:16 +000010905 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010906 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010907 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010908 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010909 n1->type = NSUBSHELL;
10910 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010911 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010912 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010913 break;
10914 case TBEGIN:
10915 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010916 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010917 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010918 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010919 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010920 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010921 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010922 }
10923
Eric Andersenc470f442003-07-28 09:56:35 +000010924 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010925 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010926
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010927 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010928 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010929 checkkwd = CHKKWD | CHKALIAS;
10930 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010931 while (readtoken() == TREDIR) {
10932 *rpp = n2 = redirnode;
10933 rpp = &n2->nfile.next;
10934 parsefname();
10935 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010936 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010937 *rpp = NULL;
10938 if (redir) {
10939 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010940 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010941 n2->type = NREDIR;
10942 n2->nredir.n = n1;
10943 n1 = n2;
10944 }
10945 n1->nredir.redirect = redir;
10946 }
Eric Andersencb57d552001-06-28 07:25:16 +000010947 return n1;
10948}
10949
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010950#if ENABLE_ASH_BASH_COMPAT
10951static int decode_dollar_squote(void)
10952{
10953 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10954 int c, cnt;
10955 char *p;
10956 char buf[4];
10957
10958 c = pgetc();
10959 p = strchr(C_escapes, c);
10960 if (p) {
10961 buf[0] = c;
10962 p = buf;
10963 cnt = 3;
10964 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10965 do {
10966 c = pgetc();
10967 *++p = c;
10968 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10969 pungetc();
10970 } else if (c == 'x') { /* \xHH */
10971 do {
10972 c = pgetc();
10973 *++p = c;
10974 } while (isxdigit(c) && --cnt);
10975 pungetc();
10976 if (cnt == 3) { /* \x but next char is "bad" */
10977 c = 'x';
10978 goto unrecognized;
10979 }
10980 } else { /* simple seq like \\ or \t */
10981 p++;
10982 }
10983 *p = '\0';
10984 p = buf;
10985 c = bb_process_escape_sequence((void*)&p);
10986 } else { /* unrecognized "\z": print both chars unless ' or " */
10987 if (c != '\'' && c != '"') {
10988 unrecognized:
10989 c |= 0x100; /* "please encode \, then me" */
10990 }
10991 }
10992 return c;
10993}
10994#endif
10995
Eric Andersencb57d552001-06-28 07:25:16 +000010996/*
10997 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10998 * is not NULL, read a here document. In the latter case, eofmark is the
10999 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011000 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011001 * is the first character of the input token or document.
11002 *
11003 * Because C does not have internal subroutines, I have simulated them
11004 * using goto's to implement the subroutine linkage. The following macros
11005 * will run code that appears at the end of readtoken1.
11006 */
Eric Andersen2870d962001-07-02 17:27:21 +000011007#define CHECKEND() {goto checkend; checkend_return:;}
11008#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11009#define PARSESUB() {goto parsesub; parsesub_return:;}
11010#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11011#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11012#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011013static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011014readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011015{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011016 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011017 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011018 char *out;
11019 int len;
11020 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011021 struct nodelist *bqlist;
11022 smallint quotef;
11023 smallint dblquote;
11024 smallint oldstyle;
11025 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011026#if ENABLE_ASH_EXPAND_PRMT
11027 smallint pssyntax; /* we are expanding a prompt string */
11028#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011029 int varnest; /* levels of variables expansion */
11030 int arinest; /* levels of arithmetic expansion */
11031 int parenlevel; /* levels of parens in arithmetic */
11032 int dqvarnest; /* levels of variables expansion within double quotes */
11033
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011034 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011035
Eric Andersencb57d552001-06-28 07:25:16 +000011036#if __GNUC__
11037 /* Avoid longjmp clobbering */
11038 (void) &out;
11039 (void) &quotef;
11040 (void) &dblquote;
11041 (void) &varnest;
11042 (void) &arinest;
11043 (void) &parenlevel;
11044 (void) &dqvarnest;
11045 (void) &oldstyle;
11046 (void) &prevsyntax;
11047 (void) &syntax;
11048#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011049 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011050 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011051 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011052 oldstyle = 0;
11053 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011054#if ENABLE_ASH_EXPAND_PRMT
11055 pssyntax = (syntax == PSSYNTAX);
11056 if (pssyntax)
11057 syntax = DQSYNTAX;
11058#endif
11059 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011060 varnest = 0;
11061 arinest = 0;
11062 parenlevel = 0;
11063 dqvarnest = 0;
11064
11065 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011066 loop:
11067 /* For each line, until end of word */
11068 {
Eric Andersenc470f442003-07-28 09:56:35 +000011069 CHECKEND(); /* set c to PEOF if at end of here document */
11070 for (;;) { /* until end of line or end of word */
11071 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011072 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011073 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000011074 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000011075 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011076 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011077 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000011078 if (doprompt)
11079 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000011080 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011081 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011082 case CWORD:
11083 USTPUTC(c, out);
11084 break;
11085 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000011086 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000011087 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011088#if ENABLE_ASH_BASH_COMPAT
11089 if (c == '\\' && bash_dollar_squote) {
11090 c = decode_dollar_squote();
11091 if (c & 0x100) {
11092 USTPUTC('\\', out);
11093 c = (unsigned char)c;
11094 }
11095 }
11096#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011097 USTPUTC(c, out);
11098 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011099 case CBACK: /* backslash */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011100 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011101 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000011102 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011103 USTPUTC('\\', out);
11104 pungetc();
11105 } else if (c == '\n') {
11106 if (doprompt)
11107 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000011108 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000011109#if ENABLE_ASH_EXPAND_PRMT
11110 if (c == '$' && pssyntax) {
11111 USTPUTC(CTLESC, out);
11112 USTPUTC('\\', out);
11113 }
11114#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011115 if (dblquote && c != '\\'
11116 && c != '`' && c != '$'
11117 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011118 ) {
11119 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011120 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011121 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011122 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000011123 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011124 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011125 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011126 }
11127 break;
11128 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011129 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011130 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000011131 if (eofmark == NULL) {
11132 USTPUTC(CTLQUOTEMARK, out);
11133 }
Eric Andersencb57d552001-06-28 07:25:16 +000011134 break;
11135 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011136 syntax = DQSYNTAX;
11137 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011138 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000011139 case CENDQUOTE:
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011140 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011141 if (eofmark != NULL && arinest == 0
11142 && varnest == 0
11143 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000011144 USTPUTC(c, out);
11145 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011146 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000011147 syntax = BASESYNTAX;
11148 dblquote = 0;
11149 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011150 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011151 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000011152 }
11153 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011154 case CVAR: /* '$' */
11155 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000011156 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011157 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000011158 if (varnest > 0) {
11159 varnest--;
11160 if (dqvarnest > 0) {
11161 dqvarnest--;
11162 }
11163 USTPUTC(CTLENDVAR, out);
11164 } else {
11165 USTPUTC(c, out);
11166 }
11167 break;
Mike Frysinger98c52642009-04-02 10:02:37 +000011168#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011169 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000011170 parenlevel++;
11171 USTPUTC(c, out);
11172 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011173 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000011174 if (parenlevel > 0) {
11175 USTPUTC(c, out);
11176 --parenlevel;
11177 } else {
11178 if (pgetc() == ')') {
11179 if (--arinest == 0) {
11180 USTPUTC(CTLENDARI, out);
11181 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011182 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011183 } else
11184 USTPUTC(')', out);
11185 } else {
11186 /*
11187 * unbalanced parens
11188 * (don't 2nd guess - no error)
11189 */
11190 pungetc();
11191 USTPUTC(')', out);
11192 }
11193 }
11194 break;
11195#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011196 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000011197 PARSEBACKQOLD();
11198 break;
Eric Andersen2870d962001-07-02 17:27:21 +000011199 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000011200 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011201 case CIGN:
11202 break;
11203 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000011204 if (varnest == 0) {
11205#if ENABLE_ASH_BASH_COMPAT
11206 if (c == '&') {
11207 if (pgetc() == '>')
11208 c = 0x100 + '>'; /* flag &> */
11209 pungetc();
11210 }
11211#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011212 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011213 }
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011214 IF_ASH_ALIAS(if (c != PEOA))
Eric Andersencb57d552001-06-28 07:25:16 +000011215 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000011216
Eric Andersencb57d552001-06-28 07:25:16 +000011217 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011218 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011219 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000011220 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011221 endword:
Mike Frysinger98c52642009-04-02 10:02:37 +000011222#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011223 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011224 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011225#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011226 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011227 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011228 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011229 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011230 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011231 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011232 }
11233 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011234 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011235 out = stackblock();
11236 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011237 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011238 && quotef == 0
11239 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011240 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011241 PARSEREDIR(); /* passed as params: out, c */
11242 lasttoken = TREDIR;
11243 return lasttoken;
11244 }
11245 /* else: non-number X seen, interpret it
11246 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011247 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011248 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011249 }
11250 quoteflag = quotef;
11251 backquotelist = bqlist;
11252 grabstackblock(len);
11253 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011254 lasttoken = TWORD;
11255 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011256/* end of readtoken routine */
11257
Eric Andersencb57d552001-06-28 07:25:16 +000011258/*
11259 * Check to see whether we are at the end of the here document. When this
11260 * is called, c is set to the first character of the next input line. If
11261 * we are at the end of the here document, this routine sets the c to PEOF.
11262 */
Eric Andersenc470f442003-07-28 09:56:35 +000011263checkend: {
11264 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011265#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011266 if (c == PEOA)
11267 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011268#endif
11269 if (striptabs) {
11270 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011271 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011272 }
Eric Andersenc470f442003-07-28 09:56:35 +000011273 }
11274 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011275 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011276 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011277
Eric Andersenc470f442003-07-28 09:56:35 +000011278 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011279 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11280 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011281 if (*p == '\n' && *q == '\0') {
11282 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011283 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011284 needprompt = doprompt;
11285 } else {
11286 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011287 }
11288 }
11289 }
11290 }
Eric Andersenc470f442003-07-28 09:56:35 +000011291 goto checkend_return;
11292}
Eric Andersencb57d552001-06-28 07:25:16 +000011293
Eric Andersencb57d552001-06-28 07:25:16 +000011294/*
11295 * Parse a redirection operator. The variable "out" points to a string
11296 * specifying the fd to be redirected. The variable "c" contains the
11297 * first character of the redirection operator.
11298 */
Eric Andersenc470f442003-07-28 09:56:35 +000011299parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011300 /* out is already checked to be a valid number or "" */
11301 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011302 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011303
Denis Vlasenko597906c2008-02-20 16:38:54 +000011304 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011305 if (c == '>') {
11306 np->nfile.fd = 1;
11307 c = pgetc();
11308 if (c == '>')
11309 np->type = NAPPEND;
11310 else if (c == '|')
11311 np->type = NCLOBBER;
11312 else if (c == '&')
11313 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011314 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011315 else {
11316 np->type = NTO;
11317 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011318 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011319 }
11320#if ENABLE_ASH_BASH_COMPAT
11321 else if (c == 0x100 + '>') { /* this flags &> redirection */
11322 np->nfile.fd = 1;
11323 pgetc(); /* this is '>', no need to check */
11324 np->type = NTO2;
11325 }
11326#endif
11327 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011328 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011329 c = pgetc();
11330 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011331 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011332 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011333 np = stzalloc(sizeof(struct nhere));
11334 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011335 }
11336 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011337 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011338 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011339 c = pgetc();
11340 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011341 heredoc->striptabs = 1;
11342 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011343 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011344 pungetc();
11345 }
11346 break;
11347
11348 case '&':
11349 np->type = NFROMFD;
11350 break;
11351
11352 case '>':
11353 np->type = NFROMTO;
11354 break;
11355
11356 default:
11357 np->type = NFROM;
11358 pungetc();
11359 break;
11360 }
Eric Andersencb57d552001-06-28 07:25:16 +000011361 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011362 if (fd >= 0)
11363 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011364 redirnode = np;
11365 goto parseredir_return;
11366}
Eric Andersencb57d552001-06-28 07:25:16 +000011367
Eric Andersencb57d552001-06-28 07:25:16 +000011368/*
11369 * Parse a substitution. At this point, we have read the dollar sign
11370 * and nothing else.
11371 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011372
11373/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11374 * (assuming ascii char codes, as the original implementation did) */
11375#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011376 (((unsigned)(c) - 33 < 32) \
11377 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011378parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011379 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011380 int typeloc;
11381 int flags;
11382 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011383 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011384
Eric Andersenc470f442003-07-28 09:56:35 +000011385 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011386 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011387 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011388 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011389#if ENABLE_ASH_BASH_COMPAT
11390 if (c == '\'')
11391 bash_dollar_squote = 1;
11392 else
11393#endif
11394 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011395 pungetc();
11396 } else if (c == '(') { /* $(command) or $((arith)) */
11397 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011398#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011399 PARSEARITH();
11400#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011401 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011402#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011403 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011404 pungetc();
11405 PARSEBACKQNEW();
11406 }
11407 } else {
11408 USTPUTC(CTLVAR, out);
11409 typeloc = out - (char *)stackblock();
11410 USTPUTC(VSNORMAL, out);
11411 subtype = VSNORMAL;
11412 if (c == '{') {
11413 c = pgetc();
11414 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011415 c = pgetc();
11416 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011417 c = '#';
11418 else
11419 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011420 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011421 subtype = 0;
11422 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011423 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011424 do {
11425 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011426 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011427 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011428 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011429 do {
11430 STPUTC(c, out);
11431 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011432 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011433 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011434 USTPUTC(c, out);
11435 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011436 } else {
11437 badsub:
11438 raise_error_syntax("bad substitution");
11439 }
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011440 if (c != '}' && subtype == VSLENGTH)
11441 goto badsub;
Eric Andersencb57d552001-06-28 07:25:16 +000011442
Eric Andersenc470f442003-07-28 09:56:35 +000011443 STPUTC('=', out);
11444 flags = 0;
11445 if (subtype == 0) {
11446 switch (c) {
11447 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011448 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011449#if ENABLE_ASH_BASH_COMPAT
11450 if (c == ':' || c == '$' || isdigit(c)) {
11451 pungetc();
11452 subtype = VSSUBSTR;
11453 break;
11454 }
11455#endif
11456 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011457 /*FALLTHROUGH*/
11458 default:
11459 p = strchr(types, c);
11460 if (p == NULL)
11461 goto badsub;
11462 subtype = p - types + VSNORMAL;
11463 break;
11464 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011465 case '#': {
11466 int cc = c;
11467 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11468 c = pgetc();
11469 if (c == cc)
11470 subtype++;
11471 else
11472 pungetc();
11473 break;
11474 }
11475#if ENABLE_ASH_BASH_COMPAT
11476 case '/':
11477 subtype = VSREPLACE;
11478 c = pgetc();
11479 if (c == '/')
11480 subtype++; /* VSREPLACEALL */
11481 else
11482 pungetc();
11483 break;
11484#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011485 }
Eric Andersenc470f442003-07-28 09:56:35 +000011486 } else {
11487 pungetc();
11488 }
11489 if (dblquote || arinest)
11490 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011491 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011492 if (subtype != VSNORMAL) {
11493 varnest++;
11494 if (dblquote || arinest) {
11495 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011496 }
11497 }
11498 }
Eric Andersenc470f442003-07-28 09:56:35 +000011499 goto parsesub_return;
11500}
Eric Andersencb57d552001-06-28 07:25:16 +000011501
Eric Andersencb57d552001-06-28 07:25:16 +000011502/*
11503 * Called to parse command substitutions. Newstyle is set if the command
11504 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11505 * list of commands (passed by reference), and savelen is the number of
11506 * characters on the top of the stack which must be preserved.
11507 */
Eric Andersenc470f442003-07-28 09:56:35 +000011508parsebackq: {
11509 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011510 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011511 union node *n;
11512 char *volatile str;
11513 struct jmploc jmploc;
11514 struct jmploc *volatile savehandler;
11515 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011516 smallint saveprompt = 0;
11517
Eric Andersencb57d552001-06-28 07:25:16 +000011518#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011519 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011520#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011521 savepbq = parsebackquote;
11522 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011523 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011524 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011525 exception_handler = savehandler;
11526 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011527 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011528 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011529 str = NULL;
11530 savelen = out - (char *)stackblock();
11531 if (savelen > 0) {
11532 str = ckmalloc(savelen);
11533 memcpy(str, stackblock(), savelen);
11534 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011535 savehandler = exception_handler;
11536 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011537 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011538 if (oldstyle) {
11539 /* We must read until the closing backquote, giving special
11540 treatment to some slashes, and then push the string and
11541 reread it as input, interpreting it normally. */
11542 char *pout;
11543 int pc;
11544 size_t psavelen;
11545 char *pstr;
11546
11547
11548 STARTSTACKSTR(pout);
11549 for (;;) {
11550 if (needprompt) {
11551 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011552 }
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++;
Eric Andersenc470f442003-07-28 09:56:35 +000011562 if (doprompt)
11563 setprompt(2);
11564 /*
11565 * If eating a newline, avoid putting
11566 * the newline into the new character
11567 * stream (via the STPUTC after the
11568 * switch).
11569 */
11570 continue;
11571 }
11572 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011573 && (!dblquote || pc != '"')
11574 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011575 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011576 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011577 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011578 break;
11579 }
11580 /* fall through */
11581
11582 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011583 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011584 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011585 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011586
11587 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011588 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011589 needprompt = doprompt;
11590 break;
11591
11592 default:
11593 break;
11594 }
11595 STPUTC(pc, pout);
11596 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011597 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011598 STPUTC('\0', pout);
11599 psavelen = pout - (char *)stackblock();
11600 if (psavelen > 0) {
11601 pstr = grabstackstr(pout);
11602 setinputstring(pstr);
11603 }
11604 }
11605 nlpp = &bqlist;
11606 while (*nlpp)
11607 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011608 *nlpp = stzalloc(sizeof(**nlpp));
11609 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011610 parsebackquote = oldstyle;
11611
11612 if (oldstyle) {
11613 saveprompt = doprompt;
11614 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011615 }
11616
Eric Andersenc470f442003-07-28 09:56:35 +000011617 n = list(2);
11618
11619 if (oldstyle)
11620 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011621 else if (readtoken() != TRP)
11622 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011623
11624 (*nlpp)->n = n;
11625 if (oldstyle) {
11626 /*
11627 * Start reading from old file again, ignoring any pushed back
11628 * tokens left from the backquote parsing
11629 */
11630 popfile();
11631 tokpushback = 0;
11632 }
11633 while (stackblocksize() <= savelen)
11634 growstackblock();
11635 STARTSTACKSTR(out);
11636 if (str) {
11637 memcpy(out, str, savelen);
11638 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011639 INT_OFF;
11640 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011641 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011642 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011643 }
11644 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011645 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011646 if (arinest || dblquote)
11647 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11648 else
11649 USTPUTC(CTLBACKQ, out);
11650 if (oldstyle)
11651 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011652 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011653}
11654
Mike Frysinger98c52642009-04-02 10:02:37 +000011655#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011656/*
11657 * Parse an arithmetic expansion (indicate start of one and set state)
11658 */
Eric Andersenc470f442003-07-28 09:56:35 +000011659parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011660 if (++arinest == 1) {
11661 prevsyntax = syntax;
11662 syntax = ARISYNTAX;
11663 USTPUTC(CTLARI, out);
11664 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011665 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011666 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011667 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011668 } else {
11669 /*
11670 * we collapse embedded arithmetic expansion to
11671 * parenthesis, which should be equivalent
11672 */
11673 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011674 }
Eric Andersenc470f442003-07-28 09:56:35 +000011675 goto parsearith_return;
11676}
11677#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011678
Eric Andersenc470f442003-07-28 09:56:35 +000011679} /* end of readtoken */
11680
Eric Andersencb57d552001-06-28 07:25:16 +000011681/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011682 * Read the next input token.
11683 * If the token is a word, we set backquotelist to the list of cmds in
11684 * backquotes. We set quoteflag to true if any part of the word was
11685 * quoted.
11686 * If the token is TREDIR, then we set redirnode to a structure containing
11687 * the redirection.
11688 * In all cases, the variable startlinno is set to the number of the line
11689 * on which the token starts.
11690 *
11691 * [Change comment: here documents and internal procedures]
11692 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11693 * word parsing code into a separate routine. In this case, readtoken
11694 * doesn't need to have any internal procedures, but parseword does.
11695 * We could also make parseoperator in essence the main routine, and
11696 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011697 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011698#define NEW_xxreadtoken
11699#ifdef NEW_xxreadtoken
11700/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011701static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011702 '\n', '(', ')', /* singles */
11703 '&', '|', ';', /* doubles */
11704 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011705};
Eric Andersencb57d552001-06-28 07:25:16 +000011706
Denis Vlasenko834dee72008-10-07 09:18:30 +000011707#define xxreadtoken_singles 3
11708#define xxreadtoken_doubles 3
11709
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011710static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011711 TNL, TLP, TRP, /* only single occurrence allowed */
11712 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11713 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011714 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011715};
11716
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011717static int
11718xxreadtoken(void)
11719{
11720 int c;
11721
11722 if (tokpushback) {
11723 tokpushback = 0;
11724 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011725 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011726 if (needprompt) {
11727 setprompt(2);
11728 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011729 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011730 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011731 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011732 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011733 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011734
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011735 if (c == '#') {
11736 while ((c = pgetc()) != '\n' && c != PEOF)
11737 continue;
11738 pungetc();
11739 } else if (c == '\\') {
11740 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011741 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011742 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011743 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011744 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011745 if (doprompt)
11746 setprompt(2);
11747 } else {
11748 const char *p;
11749
11750 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11751 if (c != PEOF) {
11752 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011753 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011754 needprompt = doprompt;
11755 }
11756
11757 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011758 if (p == NULL)
11759 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011760
Denis Vlasenko834dee72008-10-07 09:18:30 +000011761 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11762 int cc = pgetc();
11763 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011764 p += xxreadtoken_doubles + 1;
11765 } else {
11766 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011767#if ENABLE_ASH_BASH_COMPAT
11768 if (c == '&' && cc == '>') /* &> */
11769 break; /* return readtoken1(...) */
11770#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011771 }
11772 }
11773 }
11774 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11775 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011776 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011777 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011778
11779 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011780}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011781#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011782#define RETURN(token) return lasttoken = token
11783static int
11784xxreadtoken(void)
11785{
11786 int c;
11787
11788 if (tokpushback) {
11789 tokpushback = 0;
11790 return lasttoken;
11791 }
11792 if (needprompt) {
11793 setprompt(2);
11794 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011795 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011796 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011797 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011798 switch (c) {
11799 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011800 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011801 continue;
11802 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011803 while ((c = pgetc()) != '\n' && c != PEOF)
11804 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011805 pungetc();
11806 continue;
11807 case '\\':
11808 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011809 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011810 if (doprompt)
11811 setprompt(2);
11812 continue;
11813 }
11814 pungetc();
11815 goto breakloop;
11816 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011817 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011818 needprompt = doprompt;
11819 RETURN(TNL);
11820 case PEOF:
11821 RETURN(TEOF);
11822 case '&':
11823 if (pgetc() == '&')
11824 RETURN(TAND);
11825 pungetc();
11826 RETURN(TBACKGND);
11827 case '|':
11828 if (pgetc() == '|')
11829 RETURN(TOR);
11830 pungetc();
11831 RETURN(TPIPE);
11832 case ';':
11833 if (pgetc() == ';')
11834 RETURN(TENDCASE);
11835 pungetc();
11836 RETURN(TSEMI);
11837 case '(':
11838 RETURN(TLP);
11839 case ')':
11840 RETURN(TRP);
11841 default:
11842 goto breakloop;
11843 }
11844 }
11845 breakloop:
11846 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11847#undef RETURN
11848}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011849#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011850
11851static int
11852readtoken(void)
11853{
11854 int t;
11855#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011856 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011857#endif
11858
11859#if ENABLE_ASH_ALIAS
11860 top:
11861#endif
11862
11863 t = xxreadtoken();
11864
11865 /*
11866 * eat newlines
11867 */
11868 if (checkkwd & CHKNL) {
11869 while (t == TNL) {
11870 parseheredoc();
11871 t = xxreadtoken();
11872 }
11873 }
11874
11875 if (t != TWORD || quoteflag) {
11876 goto out;
11877 }
11878
11879 /*
11880 * check for keywords
11881 */
11882 if (checkkwd & CHKKWD) {
11883 const char *const *pp;
11884
11885 pp = findkwd(wordtext);
11886 if (pp) {
11887 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011888 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011889 goto out;
11890 }
11891 }
11892
11893 if (checkkwd & CHKALIAS) {
11894#if ENABLE_ASH_ALIAS
11895 struct alias *ap;
11896 ap = lookupalias(wordtext, 1);
11897 if (ap != NULL) {
11898 if (*ap->val) {
11899 pushstring(ap->val, ap);
11900 }
11901 goto top;
11902 }
11903#endif
11904 }
11905 out:
11906 checkkwd = 0;
11907#if DEBUG
11908 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011909 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011910 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011911 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011912#endif
11913 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011914}
11915
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011916static char
11917peektoken(void)
11918{
11919 int t;
11920
11921 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011922 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011923 return tokname_array[t][0];
11924}
Eric Andersencb57d552001-06-28 07:25:16 +000011925
11926/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011927 * Read and parse a command. Returns NODE_EOF on end of file.
11928 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011929 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011930static union node *
11931parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011932{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011933 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011934
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011935 tokpushback = 0;
11936 doprompt = interact;
11937 if (doprompt)
11938 setprompt(doprompt);
11939 needprompt = 0;
11940 t = readtoken();
11941 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011942 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011943 if (t == TNL)
11944 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011945 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011946 return list(1);
11947}
11948
11949/*
11950 * Input any here documents.
11951 */
11952static void
11953parseheredoc(void)
11954{
11955 struct heredoc *here;
11956 union node *n;
11957
11958 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011959 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011960
11961 while (here) {
11962 if (needprompt) {
11963 setprompt(2);
11964 }
11965 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11966 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011967 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011968 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011969 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011970 n->narg.text = wordtext;
11971 n->narg.backquote = backquotelist;
11972 here->here->nhere.doc = n;
11973 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011974 }
Eric Andersencb57d552001-06-28 07:25:16 +000011975}
11976
11977
11978/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011979 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011980 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011981#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011982static const char *
11983expandstr(const char *ps)
11984{
11985 union node n;
11986
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011987 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11988 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011989 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011990 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011991 popfile();
11992
11993 n.narg.type = NARG;
11994 n.narg.next = NULL;
11995 n.narg.text = wordtext;
11996 n.narg.backquote = backquotelist;
11997
11998 expandarg(&n, NULL, 0);
11999 return stackblock();
12000}
12001#endif
12002
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012003/*
12004 * Execute a command or commands contained in a string.
12005 */
12006static int
12007evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012008{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012009 union node *n;
12010 struct stackmark smark;
12011 int skip;
12012
12013 setinputstring(s);
12014 setstackmark(&smark);
12015
12016 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012017 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012018 evaltree(n, 0);
12019 popstackmark(&smark);
12020 skip = evalskip;
12021 if (skip)
12022 break;
12023 }
12024 popfile();
12025
12026 skip &= mask;
12027 evalskip = skip;
12028 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012029}
12030
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012031/*
12032 * The eval command.
12033 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012034static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012035evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012036{
12037 char *p;
12038 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012039
Denis Vlasenko68404f12008-03-17 09:00:54 +000012040 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012041 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012042 argv += 2;
12043 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012044 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012045 for (;;) {
12046 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012047 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012048 if (p == NULL)
12049 break;
12050 STPUTC(' ', concat);
12051 }
12052 STPUTC('\0', concat);
12053 p = grabstackstr(concat);
12054 }
12055 evalstring(p, ~SKIPEVAL);
12056
12057 }
12058 return exitstatus;
12059}
12060
12061/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012062 * Read and execute commands.
12063 * "Top" is nonzero for the top level command loop;
12064 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012065 */
12066static int
12067cmdloop(int top)
12068{
12069 union node *n;
12070 struct stackmark smark;
12071 int inter;
12072 int numeof = 0;
12073
12074 TRACE(("cmdloop(%d) called\n", top));
12075 for (;;) {
12076 int skip;
12077
12078 setstackmark(&smark);
12079#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012080 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012081 showjobs(stderr, SHOW_CHANGED);
12082#endif
12083 inter = 0;
12084 if (iflag && top) {
12085 inter++;
12086#if ENABLE_ASH_MAIL
12087 chkmail();
12088#endif
12089 }
12090 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012091#if DEBUG
12092 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012093 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012094#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012095 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012096 if (!top || numeof >= 50)
12097 break;
12098 if (!stoppedjobs()) {
12099 if (!Iflag)
12100 break;
12101 out2str("\nUse \"exit\" to leave shell.\n");
12102 }
12103 numeof++;
12104 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012105 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12106 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012107 numeof = 0;
12108 evaltree(n, 0);
12109 }
12110 popstackmark(&smark);
12111 skip = evalskip;
12112
12113 if (skip) {
12114 evalskip = 0;
12115 return skip & SKIPEVAL;
12116 }
12117 }
12118 return 0;
12119}
12120
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012121/*
12122 * Take commands from a file. To be compatible we should do a path
12123 * search for the file, which is necessary to find sub-commands.
12124 */
12125static char *
12126find_dot_file(char *name)
12127{
12128 char *fullname;
12129 const char *path = pathval();
12130 struct stat statb;
12131
12132 /* don't try this for absolute or relative paths */
12133 if (strchr(name, '/'))
12134 return name;
12135
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012136 /* IIRC standards do not say whether . is to be searched.
12137 * And it is even smaller this way, making it unconditional for now:
12138 */
12139 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12140 fullname = name;
12141 goto try_cur_dir;
12142 }
12143
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012144 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012145 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012146 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12147 /*
12148 * Don't bother freeing here, since it will
12149 * be freed by the caller.
12150 */
12151 return fullname;
12152 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012153 if (fullname != name)
12154 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012155 }
12156
12157 /* not found in the PATH */
12158 ash_msg_and_raise_error("%s: not found", name);
12159 /* NOTREACHED */
12160}
12161
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012162static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012163dotcmd(int argc, char **argv)
12164{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012165 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012166 struct strlist *sp;
12167 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012168
12169 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012170 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012171
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012172 if (!argv[1]) {
12173 /* bash says: "bash: .: filename argument required" */
12174 return 2; /* bash compat */
12175 }
12176
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012177 /* "false; . empty_file; echo $?" should print 0, not 1: */
12178 exitstatus = 0;
12179
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012180 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012181
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012182 argv += 2;
12183 argc -= 2;
12184 if (argc) { /* argc > 0, argv[0] != NULL */
12185 saveparam = shellparam;
12186 shellparam.malloced = 0;
12187 shellparam.nparam = argc;
12188 shellparam.p = argv;
12189 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012190
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012191 setinputfile(fullname, INPUT_PUSH_FILE);
12192 commandname = fullname;
12193 cmdloop(0);
12194 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012195
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012196 if (argc) {
12197 freeparam(&shellparam);
12198 shellparam = saveparam;
12199 };
12200
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012201 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012202}
12203
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012204static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012205exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012206{
12207 if (stoppedjobs())
12208 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012209 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012210 exitstatus = number(argv[1]);
12211 raise_exception(EXEXIT);
12212 /* NOTREACHED */
12213}
12214
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012215/*
12216 * Read a file containing shell functions.
12217 */
12218static void
12219readcmdfile(char *name)
12220{
12221 setinputfile(name, INPUT_PUSH_FILE);
12222 cmdloop(0);
12223 popfile();
12224}
12225
12226
Denis Vlasenkocc571512007-02-23 21:10:35 +000012227/* ============ find_command inplementation */
12228
12229/*
12230 * Resolve a command name. If you change this routine, you may have to
12231 * change the shellexec routine as well.
12232 */
12233static void
12234find_command(char *name, struct cmdentry *entry, int act, const char *path)
12235{
12236 struct tblentry *cmdp;
12237 int idx;
12238 int prev;
12239 char *fullname;
12240 struct stat statb;
12241 int e;
12242 int updatetbl;
12243 struct builtincmd *bcmd;
12244
12245 /* If name contains a slash, don't use PATH or hash table */
12246 if (strchr(name, '/') != NULL) {
12247 entry->u.index = -1;
12248 if (act & DO_ABS) {
12249 while (stat(name, &statb) < 0) {
12250#ifdef SYSV
12251 if (errno == EINTR)
12252 continue;
12253#endif
12254 entry->cmdtype = CMDUNKNOWN;
12255 return;
12256 }
12257 }
12258 entry->cmdtype = CMDNORMAL;
12259 return;
12260 }
12261
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012262/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012263
12264 updatetbl = (path == pathval());
12265 if (!updatetbl) {
12266 act |= DO_ALTPATH;
12267 if (strstr(path, "%builtin") != NULL)
12268 act |= DO_ALTBLTIN;
12269 }
12270
12271 /* If name is in the table, check answer will be ok */
12272 cmdp = cmdlookup(name, 0);
12273 if (cmdp != NULL) {
12274 int bit;
12275
12276 switch (cmdp->cmdtype) {
12277 default:
12278#if DEBUG
12279 abort();
12280#endif
12281 case CMDNORMAL:
12282 bit = DO_ALTPATH;
12283 break;
12284 case CMDFUNCTION:
12285 bit = DO_NOFUNC;
12286 break;
12287 case CMDBUILTIN:
12288 bit = DO_ALTBLTIN;
12289 break;
12290 }
12291 if (act & bit) {
12292 updatetbl = 0;
12293 cmdp = NULL;
12294 } else if (cmdp->rehash == 0)
12295 /* if not invalidated by cd, we're done */
12296 goto success;
12297 }
12298
12299 /* If %builtin not in path, check for builtin next */
12300 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012301 if (bcmd) {
12302 if (IS_BUILTIN_REGULAR(bcmd))
12303 goto builtin_success;
12304 if (act & DO_ALTPATH) {
12305 if (!(act & DO_ALTBLTIN))
12306 goto builtin_success;
12307 } else if (builtinloc <= 0) {
12308 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012309 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012310 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012311
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012312#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012313 {
12314 int applet_no = find_applet_by_name(name);
12315 if (applet_no >= 0) {
12316 entry->cmdtype = CMDNORMAL;
12317 entry->u.index = -2 - applet_no;
12318 return;
12319 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012320 }
12321#endif
12322
Denis Vlasenkocc571512007-02-23 21:10:35 +000012323 /* We have to search path. */
12324 prev = -1; /* where to start */
12325 if (cmdp && cmdp->rehash) { /* doing a rehash */
12326 if (cmdp->cmdtype == CMDBUILTIN)
12327 prev = builtinloc;
12328 else
12329 prev = cmdp->param.index;
12330 }
12331
12332 e = ENOENT;
12333 idx = -1;
12334 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012335 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012336 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012337 /* NB: code below will still use fullname
12338 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012339 idx++;
12340 if (pathopt) {
12341 if (prefix(pathopt, "builtin")) {
12342 if (bcmd)
12343 goto builtin_success;
12344 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012345 }
12346 if ((act & DO_NOFUNC)
12347 || !prefix(pathopt, "func")
12348 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012349 continue;
12350 }
12351 }
12352 /* if rehash, don't redo absolute path names */
12353 if (fullname[0] == '/' && idx <= prev) {
12354 if (idx < prev)
12355 continue;
12356 TRACE(("searchexec \"%s\": no change\n", name));
12357 goto success;
12358 }
12359 while (stat(fullname, &statb) < 0) {
12360#ifdef SYSV
12361 if (errno == EINTR)
12362 continue;
12363#endif
12364 if (errno != ENOENT && errno != ENOTDIR)
12365 e = errno;
12366 goto loop;
12367 }
12368 e = EACCES; /* if we fail, this will be the error */
12369 if (!S_ISREG(statb.st_mode))
12370 continue;
12371 if (pathopt) { /* this is a %func directory */
12372 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012373 /* NB: stalloc will return space pointed by fullname
12374 * (because we don't have any intervening allocations
12375 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012376 readcmdfile(fullname);
12377 cmdp = cmdlookup(name, 0);
12378 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12379 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12380 stunalloc(fullname);
12381 goto success;
12382 }
12383 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12384 if (!updatetbl) {
12385 entry->cmdtype = CMDNORMAL;
12386 entry->u.index = idx;
12387 return;
12388 }
12389 INT_OFF;
12390 cmdp = cmdlookup(name, 1);
12391 cmdp->cmdtype = CMDNORMAL;
12392 cmdp->param.index = idx;
12393 INT_ON;
12394 goto success;
12395 }
12396
12397 /* We failed. If there was an entry for this command, delete it */
12398 if (cmdp && updatetbl)
12399 delete_cmd_entry();
12400 if (act & DO_ERR)
12401 ash_msg("%s: %s", name, errmsg(e, "not found"));
12402 entry->cmdtype = CMDUNKNOWN;
12403 return;
12404
12405 builtin_success:
12406 if (!updatetbl) {
12407 entry->cmdtype = CMDBUILTIN;
12408 entry->u.cmd = bcmd;
12409 return;
12410 }
12411 INT_OFF;
12412 cmdp = cmdlookup(name, 1);
12413 cmdp->cmdtype = CMDBUILTIN;
12414 cmdp->param.cmd = bcmd;
12415 INT_ON;
12416 success:
12417 cmdp->rehash = 0;
12418 entry->cmdtype = cmdp->cmdtype;
12419 entry->u = cmdp->param;
12420}
12421
12422
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012423/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012424
Eric Andersencb57d552001-06-28 07:25:16 +000012425/*
Eric Andersencb57d552001-06-28 07:25:16 +000012426 * The trap builtin.
12427 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012428static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012429trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012430{
12431 char *action;
12432 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012433 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012434
Eric Andersenc470f442003-07-28 09:56:35 +000012435 nextopt(nullstr);
12436 ap = argptr;
12437 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012438 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012439 char *tr = trap_ptr[signo];
12440 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012441 /* note: bash adds "SIG", but only if invoked
12442 * as "bash". If called as "sh", or if set -o posix,
12443 * then it prints short signal names.
12444 * We are printing short names: */
12445 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012446 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012447 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012448 /* trap_ptr != trap only if we are in special-cased `trap` code.
12449 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012450 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012451 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012452 }
12453 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012454 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012455 if (trap_ptr != trap) {
12456 free(trap_ptr);
12457 trap_ptr = trap;
12458 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012459 */
Eric Andersencb57d552001-06-28 07:25:16 +000012460 return 0;
12461 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012462
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012463 action = NULL;
12464 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012465 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012466 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012467 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012468 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012469 if (signo < 0) {
12470 /* Mimic bash message exactly */
12471 ash_msg("%s: invalid signal specification", *ap);
12472 exitcode = 1;
12473 goto next;
12474 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012475 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012476 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012477 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012478 action = NULL;
12479 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012480 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012481 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012482 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012483 if (action)
12484 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012485 trap[signo] = action;
12486 if (signo != 0)
12487 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012488 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012489 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012490 ap++;
12491 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012492 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012493}
12494
Eric Andersenc470f442003-07-28 09:56:35 +000012495
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012496/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012497
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012498#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012499/*
12500 * Lists available builtins
12501 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012502static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012503helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012504{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012505 unsigned col;
12506 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012507
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012508 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012509 "Built-in commands:\n"
12510 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012511 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012512 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012513 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012514 if (col > 60) {
12515 out1fmt("\n");
12516 col = 0;
12517 }
12518 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012519#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012520 {
12521 const char *a = applet_names;
12522 while (*a) {
12523 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12524 if (col > 60) {
12525 out1fmt("\n");
12526 col = 0;
12527 }
12528 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012529 }
12530 }
12531#endif
12532 out1fmt("\n\n");
12533 return EXIT_SUCCESS;
12534}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012535#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012536
Eric Andersencb57d552001-06-28 07:25:16 +000012537/*
Eric Andersencb57d552001-06-28 07:25:16 +000012538 * The export and readonly commands.
12539 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012540static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012541exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012542{
12543 struct var *vp;
12544 char *name;
12545 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012546 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012547 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012548
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012549 if (nextopt("p") != 'p') {
12550 aptr = argptr;
12551 name = *aptr;
12552 if (name) {
12553 do {
12554 p = strchr(name, '=');
12555 if (p != NULL) {
12556 p++;
12557 } else {
12558 vp = *findvar(hashvar(name), name);
12559 if (vp) {
12560 vp->flags |= flag;
12561 continue;
12562 }
Eric Andersencb57d552001-06-28 07:25:16 +000012563 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012564 setvar(name, p, flag);
12565 } while ((name = *++aptr) != NULL);
12566 return 0;
12567 }
Eric Andersencb57d552001-06-28 07:25:16 +000012568 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012569 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012570 return 0;
12571}
12572
Eric Andersencb57d552001-06-28 07:25:16 +000012573/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012574 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012575 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012576static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012577unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012578{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012579 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012580
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012581 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012582 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012583 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012584}
12585
Eric Andersencb57d552001-06-28 07:25:16 +000012586/*
Eric Andersencb57d552001-06-28 07:25:16 +000012587 * The unset builtin command. We unset the function before we unset the
12588 * variable to allow a function to be unset when there is a readonly variable
12589 * with the same name.
12590 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012591static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012592unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012593{
12594 char **ap;
12595 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012596 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012597 int ret = 0;
12598
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012599 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012600 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012601 }
Eric Andersencb57d552001-06-28 07:25:16 +000012602
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012603 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012604 if (flag != 'f') {
12605 i = unsetvar(*ap);
12606 ret |= i;
12607 if (!(i & 2))
12608 continue;
12609 }
12610 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012611 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012612 }
Eric Andersenc470f442003-07-28 09:56:35 +000012613 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012614}
12615
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012616static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012617 ' ', offsetof(struct tms, tms_utime),
12618 '\n', offsetof(struct tms, tms_stime),
12619 ' ', offsetof(struct tms, tms_cutime),
12620 '\n', offsetof(struct tms, tms_cstime),
12621 0
12622};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012623static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012624timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012625{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012626 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012627 const unsigned char *p;
12628 struct tms buf;
12629
12630 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012631 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012632
12633 p = timescmd_str;
12634 do {
12635 t = *(clock_t *)(((char *) &buf) + p[1]);
12636 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012637 t = t % clk_tck;
12638 out1fmt("%lum%lu.%03lus%c",
12639 s / 60, s % 60,
12640 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012641 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012642 p += 2;
12643 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012644
Eric Andersencb57d552001-06-28 07:25:16 +000012645 return 0;
12646}
12647
Mike Frysinger98c52642009-04-02 10:02:37 +000012648#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012649/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012650 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012651 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012652 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012653 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012654 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012655static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012656letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012657{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012658 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012659
Denis Vlasenko68404f12008-03-17 09:00:54 +000012660 argv++;
12661 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012662 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012663 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012664 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012665 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012666
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012667 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012668}
Eric Andersenc470f442003-07-28 09:56:35 +000012669#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012670
Eric Andersenc470f442003-07-28 09:56:35 +000012671/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012672 * The read builtin. Options:
12673 * -r Do not interpret '\' specially
12674 * -s Turn off echo (tty only)
12675 * -n NCHARS Read NCHARS max
12676 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12677 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12678 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012679 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012680 * TODO: bash also has:
12681 * -a ARRAY Read into array[0],[1],etc
12682 * -d DELIM End on DELIM char, not newline
12683 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012684 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012685static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012686readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012687{
Denys Vlasenko73067272010-01-12 22:11:24 +010012688 char *opt_n = NULL;
12689 char *opt_p = NULL;
12690 char *opt_t = NULL;
12691 char *opt_u = NULL;
12692 int read_flags = 0;
12693 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012694 int i;
12695
Denys Vlasenko73067272010-01-12 22:11:24 +010012696 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012697 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012698 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012699 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012700 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012701 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012702 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012703 break;
12704 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012705 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012706 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012707 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012708 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012709 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012710 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012711 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012712 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012713 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012714 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012715 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012716 default:
12717 break;
12718 }
Eric Andersenc470f442003-07-28 09:56:35 +000012719 }
Paul Fox02eb9342005-09-07 16:56:02 +000012720
Denys Vlasenko03dad222010-01-12 23:29:57 +010012721 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012722 argptr,
12723 bltinlookup("IFS"), /* can be NULL */
12724 read_flags,
12725 opt_n,
12726 opt_p,
12727 opt_t,
12728 opt_u
12729 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012730
Denys Vlasenko73067272010-01-12 22:11:24 +010012731 if ((uintptr_t)r > 1)
12732 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012733
Denys Vlasenko73067272010-01-12 22:11:24 +010012734 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012735}
12736
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012737static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012738umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012739{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012740 static const char permuser[3] ALIGN1 = "ugo";
12741 static const char permmode[3] ALIGN1 = "rwx";
12742 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012743 S_IRUSR, S_IWUSR, S_IXUSR,
12744 S_IRGRP, S_IWGRP, S_IXGRP,
12745 S_IROTH, S_IWOTH, S_IXOTH
12746 };
12747
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012748 /* TODO: use bb_parse_mode() instead */
12749
Eric Andersenc470f442003-07-28 09:56:35 +000012750 char *ap;
12751 mode_t mask;
12752 int i;
12753 int symbolic_mode = 0;
12754
12755 while (nextopt("S") != '\0') {
12756 symbolic_mode = 1;
12757 }
12758
Denis Vlasenkob012b102007-02-19 22:43:01 +000012759 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012760 mask = umask(0);
12761 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012762 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012763
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012764 ap = *argptr;
12765 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012766 if (symbolic_mode) {
12767 char buf[18];
12768 char *p = buf;
12769
12770 for (i = 0; i < 3; i++) {
12771 int j;
12772
12773 *p++ = permuser[i];
12774 *p++ = '=';
12775 for (j = 0; j < 3; j++) {
12776 if ((mask & permmask[3 * i + j]) == 0) {
12777 *p++ = permmode[j];
12778 }
12779 }
12780 *p++ = ',';
12781 }
12782 *--p = 0;
12783 puts(buf);
12784 } else {
12785 out1fmt("%.4o\n", mask);
12786 }
12787 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012788 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012789 mask = 0;
12790 do {
12791 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012792 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012793 mask = (mask << 3) + (*ap - '0');
12794 } while (*++ap != '\0');
12795 umask(mask);
12796 } else {
12797 mask = ~mask & 0777;
12798 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012799 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012800 }
12801 umask(~mask & 0777);
12802 }
12803 }
12804 return 0;
12805}
12806
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012807static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012808ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012809{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012810 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012811}
12812
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012813/* ============ main() and helpers */
12814
12815/*
12816 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012817 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012818static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012819static void
12820exitshell(void)
12821{
12822 struct jmploc loc;
12823 char *p;
12824 int status;
12825
12826 status = exitstatus;
12827 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12828 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012829 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012830/* dash bug: it just does _exit(exitstatus) here
12831 * but we have to do setjobctl(0) first!
12832 * (bug is still not fixed in dash-0.5.3 - if you run dash
12833 * under Midnight Commander, on exit from dash MC is backgrounded) */
12834 status = exitstatus;
12835 goto out;
12836 }
12837 exception_handler = &loc;
12838 p = trap[0];
12839 if (p) {
12840 trap[0] = NULL;
12841 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012842 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012843 }
12844 flush_stdout_stderr();
12845 out:
12846 setjobctl(0);
12847 _exit(status);
12848 /* NOTREACHED */
12849}
12850
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012851static void
12852init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012853{
12854 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012855 /* we will never free this */
12856 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012857
12858 /* from trap.c: */
12859 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012860 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12861 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12862 */
12863 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012864
12865 /* from var.c: */
12866 {
12867 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012868 const char *p;
12869 struct stat st1, st2;
12870
12871 initvar();
12872 for (envp = environ; envp && *envp; envp++) {
12873 if (strchr(*envp, '=')) {
12874 setvareq(*envp, VEXPORT|VTEXTFIXED);
12875 }
12876 }
12877
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012878 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012879
12880 p = lookupvar("PWD");
12881 if (p)
12882 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12883 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12884 p = '\0';
12885 setpwd(p, 0);
12886 }
12887}
12888
12889/*
12890 * Process the shell command line arguments.
12891 */
12892static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012893procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012894{
12895 int i;
12896 const char *xminusc;
12897 char **xargv;
12898
12899 xargv = argv;
12900 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012901 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012902 xargv++;
12903 for (i = 0; i < NOPTS; i++)
12904 optlist[i] = 2;
12905 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012906 if (options(1)) {
12907 /* it already printed err message */
12908 raise_exception(EXERROR);
12909 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012910 xargv = argptr;
12911 xminusc = minusc;
12912 if (*xargv == NULL) {
12913 if (xminusc)
12914 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12915 sflag = 1;
12916 }
12917 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12918 iflag = 1;
12919 if (mflag == 2)
12920 mflag = iflag;
12921 for (i = 0; i < NOPTS; i++)
12922 if (optlist[i] == 2)
12923 optlist[i] = 0;
12924#if DEBUG == 2
12925 debug = 1;
12926#endif
12927 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12928 if (xminusc) {
12929 minusc = *xargv++;
12930 if (*xargv)
12931 goto setarg0;
12932 } else if (!sflag) {
12933 setinputfile(*xargv, 0);
12934 setarg0:
12935 arg0 = *xargv++;
12936 commandname = arg0;
12937 }
12938
12939 shellparam.p = xargv;
12940#if ENABLE_ASH_GETOPTS
12941 shellparam.optind = 1;
12942 shellparam.optoff = -1;
12943#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012944 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012945 while (*xargv) {
12946 shellparam.nparam++;
12947 xargv++;
12948 }
12949 optschanged();
12950}
12951
12952/*
12953 * Read /etc/profile or .profile.
12954 */
12955static void
12956read_profile(const char *name)
12957{
12958 int skip;
12959
12960 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12961 return;
12962 skip = cmdloop(0);
12963 popfile();
12964 if (skip)
12965 exitshell();
12966}
12967
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012968/*
12969 * This routine is called when an error or an interrupt occurs in an
12970 * interactive shell and control is returned to the main command loop.
12971 */
12972static void
12973reset(void)
12974{
12975 /* from eval.c: */
12976 evalskip = 0;
12977 loopnest = 0;
12978 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000012979 g_parsefile->left_in_buffer = 0;
12980 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012981 popallfiles();
12982 /* from parser.c: */
12983 tokpushback = 0;
12984 checkkwd = 0;
12985 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000012986 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012987}
12988
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012989#if PROFILE
12990static short profile_buf[16384];
12991extern int etext();
12992#endif
12993
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012994/*
12995 * Main routine. We initialize things, parse the arguments, execute
12996 * profiles if we're a login shell, and then call cmdloop to execute
12997 * commands. The setjmp call sets up the location to jump to when an
12998 * exception occurs. When an exception occurs the variable "state"
12999 * is used to figure out how far we had gotten.
13000 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013001int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013002int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013003{
Mike Frysinger98c52642009-04-02 10:02:37 +000013004 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013005 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013006 struct jmploc jmploc;
13007 struct stackmark smark;
13008
Denis Vlasenko01631112007-12-16 17:20:38 +000013009 /* Initialize global data */
13010 INIT_G_misc();
13011 INIT_G_memstack();
13012 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013013#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013014 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013015#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013016 INIT_G_cmdtable();
13017
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013018#if PROFILE
13019 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13020#endif
13021
13022#if ENABLE_FEATURE_EDITING
13023 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13024#endif
13025 state = 0;
13026 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013027 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013028 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013029
13030 reset();
13031
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013032 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013033 if (e == EXERROR)
13034 exitstatus = 2;
13035 s = state;
13036 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13037 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013038 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013039 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013040
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013041 popstackmark(&smark);
13042 FORCE_INT_ON; /* enable interrupts */
13043 if (s == 1)
13044 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013045 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013046 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013047 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013048 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013049 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013050 }
13051 exception_handler = &jmploc;
13052#if DEBUG
13053 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013054 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013055 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013056#endif
13057 rootpid = getpid();
13058
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013059 init();
13060 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013061 procargs(argv);
13062
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013063#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13064 if (iflag) {
13065 const char *hp = lookupvar("HISTFILE");
13066
13067 if (hp == NULL) {
13068 hp = lookupvar("HOME");
13069 if (hp != NULL) {
13070 char *defhp = concat_path_file(hp, ".ash_history");
13071 setvar("HISTFILE", defhp, 0);
13072 free(defhp);
13073 }
13074 }
13075 }
13076#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013077 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013078 isloginsh = 1;
13079 if (isloginsh) {
13080 state = 1;
13081 read_profile("/etc/profile");
13082 state1:
13083 state = 2;
13084 read_profile(".profile");
13085 }
13086 state2:
13087 state = 3;
13088 if (
13089#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013090 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013091#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013092 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013093 ) {
13094 shinit = lookupvar("ENV");
13095 if (shinit != NULL && *shinit != '\0') {
13096 read_profile(shinit);
13097 }
13098 }
13099 state3:
13100 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013101 if (minusc) {
13102 /* evalstring pushes parsefile stack.
13103 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013104 * is one of stacked source fds.
13105 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013106 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013107 // ^^ not necessary since now we special-case fd 0
13108 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013109 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013110 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013111
13112 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013113#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013114 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013115 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013116 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013117 line_input_state->hist_file = hp;
13118 }
13119#endif
13120 state4: /* XXX ??? - why isn't this before the "if" statement */
13121 cmdloop(1);
13122 }
13123#if PROFILE
13124 monitor(0);
13125#endif
13126#ifdef GPROF
13127 {
13128 extern void _mcleanup(void);
13129 _mcleanup();
13130 }
13131#endif
13132 exitshell();
13133 /* NOTREACHED */
13134}
13135
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013136
Eric Andersendf82f612001-06-28 07:46:40 +000013137/*-
13138 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013139 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013140 *
13141 * This code is derived from software contributed to Berkeley by
13142 * Kenneth Almquist.
13143 *
13144 * Redistribution and use in source and binary forms, with or without
13145 * modification, are permitted provided that the following conditions
13146 * are met:
13147 * 1. Redistributions of source code must retain the above copyright
13148 * notice, this list of conditions and the following disclaimer.
13149 * 2. Redistributions in binary form must reproduce the above copyright
13150 * notice, this list of conditions and the following disclaimer in the
13151 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013152 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013153 * may be used to endorse or promote products derived from this software
13154 * without specific prior written permission.
13155 *
13156 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13157 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13158 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13159 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13160 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13161 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13162 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13163 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13164 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13165 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13166 * SUCH DAMAGE.
13167 */