blob: c8e7e3b069a88027847e601f7e159eb135aa7df5 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000039#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000040#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020043#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010044
45#include "shell_common.h"
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.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200182//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200183
184//usage:#define ash_trivial_usage NOUSAGE_STR
185//usage:#define ash_full_usage ""
186//usage:#define sh_trivial_usage NOUSAGE_STR
187//usage:#define sh_full_usage ""
188//usage:#define bash_trivial_usage NOUSAGE_STR
189//usage:#define bash_full_usage ""
190
Denis Vlasenkob012b102007-02-19 22:43:01 +0000191
Denis Vlasenko01631112007-12-16 17:20:38 +0000192/* ============ Hash table sizes. Configurable. */
193
194#define VTABSIZE 39
195#define ATABSIZE 39
196#define CMDTABLESIZE 31 /* should be prime */
197
198
Denis Vlasenkob012b102007-02-19 22:43:01 +0000199/* ============ Shell options */
200
201static const char *const optletters_optnames[] = {
202 "e" "errexit",
203 "f" "noglob",
204 "I" "ignoreeof",
205 "i" "interactive",
206 "m" "monitor",
207 "n" "noexec",
208 "s" "stdin",
209 "x" "xtrace",
210 "v" "verbose",
211 "C" "noclobber",
212 "a" "allexport",
213 "b" "notify",
214 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100215 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100216#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100217 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100218#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000219#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000220 ,"\0" "nolog"
221 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000222#endif
223};
224
Denys Vlasenko285ad152009-12-04 23:02:27 +0100225#define optletters(n) optletters_optnames[n][0]
226#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000227
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000228enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000229
Eric Andersenc470f442003-07-28 09:56:35 +0000230
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000232
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200233#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000234
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000235/*
Eric Andersenc470f442003-07-28 09:56:35 +0000236 * We enclose jmp_buf in a structure so that we can declare pointers to
237 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000238 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000239 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000240 * exception handlers, the user should save the value of handler on entry
241 * to an inner scope, set handler to point to a jmploc structure for the
242 * inner scope, and restore handler on exit from the scope.
243 */
Eric Andersenc470f442003-07-28 09:56:35 +0000244struct jmploc {
245 jmp_buf loc;
246};
Denis Vlasenko01631112007-12-16 17:20:38 +0000247
248struct globals_misc {
249 /* pid of main shell */
250 int rootpid;
251 /* shell level: 0 for the main shell, 1 for its children, and so on */
252 int shlvl;
253#define rootshell (!shlvl)
254 char *minusc; /* argument to -c option */
255
256 char *curdir; // = nullstr; /* current working directory */
257 char *physdir; // = nullstr; /* physical working directory */
258
259 char *arg0; /* value of $0 */
260
261 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000262
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200263 volatile int suppress_int; /* counter */
264 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000265 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200266 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000267 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000268 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000269#define EXINT 0 /* SIGINT received */
270#define EXERROR 1 /* a generic error */
271#define EXSHELLPROC 2 /* execute a shell procedure */
272#define EXEXEC 3 /* command execution failed */
273#define EXEXIT 4 /* exit the shell */
274#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000275
Denis Vlasenko01631112007-12-16 17:20:38 +0000276 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000277 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000278
279 char optlist[NOPTS];
280#define eflag optlist[0]
281#define fflag optlist[1]
282#define Iflag optlist[2]
283#define iflag optlist[3]
284#define mflag optlist[4]
285#define nflag optlist[5]
286#define sflag optlist[6]
287#define xflag optlist[7]
288#define vflag optlist[8]
289#define Cflag optlist[9]
290#define aflag optlist[10]
291#define bflag optlist[11]
292#define uflag optlist[12]
293#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100294#if ENABLE_ASH_BASH_COMPAT
295# define pipefail optlist[14]
296#else
297# define pipefail 0
298#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000299#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100300# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
301# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000302#endif
303
304 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000305 /*
306 * Sigmode records the current value of the signal handlers for the various
307 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000308 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000309 */
310 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000311#define S_DFL 1 /* default signal handling (SIG_DFL) */
312#define S_CATCH 2 /* signal is caught */
313#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000314#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000315
Denis Vlasenko01631112007-12-16 17:20:38 +0000316 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000317 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200318 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000319 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200320 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000321
322 /* Rarely referenced stuff */
323#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200324 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000325#endif
326 pid_t backgndpid; /* pid of last background process */
327 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000328};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000329extern struct globals_misc *const ash_ptr_to_globals_misc;
330#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000331#define rootpid (G_misc.rootpid )
332#define shlvl (G_misc.shlvl )
333#define minusc (G_misc.minusc )
334#define curdir (G_misc.curdir )
335#define physdir (G_misc.physdir )
336#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000337#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000338#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200339#define suppress_int (G_misc.suppress_int )
340#define pending_int (G_misc.pending_int )
341#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000342#define isloginsh (G_misc.isloginsh )
343#define nullstr (G_misc.nullstr )
344#define optlist (G_misc.optlist )
345#define sigmode (G_misc.sigmode )
346#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200347#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000348#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200349#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200350#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000351#define backgndpid (G_misc.backgndpid )
352#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000353#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000354 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
355 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000356 curdir = nullstr; \
357 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200358 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000359} while (0)
360
361
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000362/* ============ DEBUG */
363#if DEBUG
364static void trace_printf(const char *fmt, ...);
365static void trace_vprintf(const char *fmt, va_list va);
366# define TRACE(param) trace_printf param
367# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000368# define close(fd) do { \
369 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000370 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200371 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000372 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000373} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000374#else
375# define TRACE(param)
376# define TRACEV(param)
377#endif
378
379
Denis Vlasenko559691a2008-10-05 18:39:31 +0000380/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000381#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
382
Denis Vlasenko559691a2008-10-05 18:39:31 +0000383static int isdigit_str9(const char *str)
384{
385 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
386 while (--maxlen && isdigit(*str))
387 str++;
388 return (*str == '\0');
389}
Denis Vlasenko01631112007-12-16 17:20:38 +0000390
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200391static const char *var_end(const char *var)
392{
393 while (*var)
394 if (*var++ == '=')
395 break;
396 return var;
397}
398
Denis Vlasenko559691a2008-10-05 18:39:31 +0000399
400/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000401/*
Eric Andersen2870d962001-07-02 17:27:21 +0000402 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000403 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000404 * much more efficient and portable. (But hacking the kernel is so much
405 * more fun than worrying about efficiency and portability. :-))
406 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000407#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200408 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000409 xbarrier(); \
410} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000411
412/*
413 * Called to raise an exception. Since C doesn't include exceptions, we
414 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000415 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000416 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000417static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000418static void
419raise_exception(int e)
420{
421#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000422 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000423 abort();
424#endif
425 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000426 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000427 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000428}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000429#if DEBUG
430#define raise_exception(e) do { \
431 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
432 raise_exception(e); \
433} while (0)
434#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000435
436/*
437 * Called from trap.c when a SIGINT is received. (If the user specifies
438 * that SIGINT is to be trapped or ignored using the trap builtin, then
439 * this routine is not called.) Suppressint is nonzero when interrupts
440 * are held using the INT_OFF macro. (The test for iflag is just
441 * defensive programming.)
442 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000443static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000444static void
445raise_interrupt(void)
446{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000447 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000448
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200449 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000450 /* Signal is not automatically unmasked after it is raised,
451 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000452 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200453 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000454
Denis Vlasenko4b875702009-03-19 13:30:04 +0000455 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000456 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
457 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000458 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000459 signal(SIGINT, SIG_DFL);
460 raise(SIGINT);
461 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000462 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000463 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000464 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000465 /* NOTREACHED */
466}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000467#if DEBUG
468#define raise_interrupt() do { \
469 TRACE(("raising interrupt on line %d\n", __LINE__)); \
470 raise_interrupt(); \
471} while (0)
472#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000473
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000474static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000475int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000476{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000477 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200478 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000479 raise_interrupt();
480 }
481}
482#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000483static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000484force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000485{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000486 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200487 suppress_int = 0;
488 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000489 raise_interrupt();
490}
491#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000492
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200493#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000494
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000495#define RESTORE_INT(v) do { \
496 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200497 suppress_int = (v); \
498 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000499 raise_interrupt(); \
500} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000501
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000502
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000503/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000504
Eric Andersenc470f442003-07-28 09:56:35 +0000505static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000506outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000507{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000508 INT_OFF;
509 fputs(p, file);
510 INT_ON;
511}
512
513static void
514flush_stdout_stderr(void)
515{
516 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100517 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000518 INT_ON;
519}
520
521static void
522outcslow(int c, FILE *dest)
523{
524 INT_OFF;
525 putc(c, dest);
526 fflush(dest);
527 INT_ON;
528}
529
530static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
531static int
532out1fmt(const char *fmt, ...)
533{
534 va_list ap;
535 int r;
536
537 INT_OFF;
538 va_start(ap, fmt);
539 r = vprintf(fmt, ap);
540 va_end(ap);
541 INT_ON;
542 return r;
543}
544
545static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
546static int
547fmtstr(char *outbuf, size_t length, const char *fmt, ...)
548{
549 va_list ap;
550 int ret;
551
552 va_start(ap, fmt);
553 INT_OFF;
554 ret = vsnprintf(outbuf, length, fmt, ap);
555 va_end(ap);
556 INT_ON;
557 return ret;
558}
559
560static void
561out1str(const char *p)
562{
563 outstr(p, stdout);
564}
565
566static void
567out2str(const char *p)
568{
569 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100570 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000571}
572
573
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000574/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000575
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000576/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100577#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200578#define CTLESC ((unsigned char)'\201') /* escape next character */
579#define CTLVAR ((unsigned char)'\202') /* variable defn */
580#define CTLENDVAR ((unsigned char)'\203')
581#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000582#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
583/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200584#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
585#define CTLENDARI ((unsigned char)'\207')
586#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100587#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000588
589/* variable substitution byte (follows CTLVAR) */
590#define VSTYPE 0x0f /* type of variable substitution */
591#define VSNUL 0x10 /* colon--treat the empty string as unset */
592#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
593
594/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000595#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
596#define VSMINUS 0x2 /* ${var-text} */
597#define VSPLUS 0x3 /* ${var+text} */
598#define VSQUESTION 0x4 /* ${var?message} */
599#define VSASSIGN 0x5 /* ${var=text} */
600#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
601#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
602#define VSTRIMLEFT 0x8 /* ${var#pattern} */
603#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
604#define VSLENGTH 0xa /* ${#var} */
605#if ENABLE_ASH_BASH_COMPAT
606#define VSSUBSTR 0xc /* ${var:position:length} */
607#define VSREPLACE 0xd /* ${var/pattern/replacement} */
608#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
609#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000610
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000611static const char dolatstr[] ALIGN1 = {
612 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
613};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000614
Denis Vlasenko559691a2008-10-05 18:39:31 +0000615#define NCMD 0
616#define NPIPE 1
617#define NREDIR 2
618#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000619#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620#define NAND 5
621#define NOR 6
622#define NSEMI 7
623#define NIF 8
624#define NWHILE 9
625#define NUNTIL 10
626#define NFOR 11
627#define NCASE 12
628#define NCLIST 13
629#define NDEFUN 14
630#define NARG 15
631#define NTO 16
632#if ENABLE_ASH_BASH_COMPAT
633#define NTO2 17
634#endif
635#define NCLOBBER 18
636#define NFROM 19
637#define NFROMTO 20
638#define NAPPEND 21
639#define NTOFD 22
640#define NFROMFD 23
641#define NHERE 24
642#define NXHERE 25
643#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000644#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000645
646union node;
647
648struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000649 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000650 union node *assign;
651 union node *args;
652 union node *redirect;
653};
654
655struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000656 smallint type;
657 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000658 struct nodelist *cmdlist;
659};
660
661struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000662 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000663 union node *n;
664 union node *redirect;
665};
666
667struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000668 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000669 union node *ch1;
670 union node *ch2;
671};
672
673struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000674 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000675 union node *test;
676 union node *ifpart;
677 union node *elsepart;
678};
679
680struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000681 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000682 union node *args;
683 union node *body;
684 char *var;
685};
686
687struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000688 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000689 union node *expr;
690 union node *cases;
691};
692
693struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000694 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000695 union node *next;
696 union node *pattern;
697 union node *body;
698};
699
700struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000701 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000702 union node *next;
703 char *text;
704 struct nodelist *backquote;
705};
706
Denis Vlasenko559691a2008-10-05 18:39:31 +0000707/* nfile and ndup layout must match!
708 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
709 * that it is actually NTO2 (>&file), and change its type.
710 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000711struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000712 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000713 union node *next;
714 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000715 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000716 union node *fname;
717 char *expfname;
718};
719
720struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000721 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000722 union node *next;
723 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000724 int dupfd;
725 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000726 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000727};
728
729struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000730 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000731 union node *next;
732 int fd;
733 union node *doc;
734};
735
736struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000737 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000738 union node *com;
739};
740
741union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000742 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000743 struct ncmd ncmd;
744 struct npipe npipe;
745 struct nredir nredir;
746 struct nbinary nbinary;
747 struct nif nif;
748 struct nfor nfor;
749 struct ncase ncase;
750 struct nclist nclist;
751 struct narg narg;
752 struct nfile nfile;
753 struct ndup ndup;
754 struct nhere nhere;
755 struct nnot nnot;
756};
757
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200758/*
759 * NODE_EOF is returned by parsecmd when it encounters an end of file.
760 * It must be distinct from NULL.
761 */
762#define NODE_EOF ((union node *) -1L)
763
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000764struct nodelist {
765 struct nodelist *next;
766 union node *n;
767};
768
769struct funcnode {
770 int count;
771 union node n;
772};
773
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000774/*
775 * Free a parse tree.
776 */
777static void
778freefunc(struct funcnode *f)
779{
780 if (f && --f->count < 0)
781 free(f);
782}
783
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000784
785/* ============ Debugging output */
786
787#if DEBUG
788
789static FILE *tracefile;
790
791static void
792trace_printf(const char *fmt, ...)
793{
794 va_list va;
795
796 if (debug != 1)
797 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000798 if (DEBUG_TIME)
799 fprintf(tracefile, "%u ", (int) time(NULL));
800 if (DEBUG_PID)
801 fprintf(tracefile, "[%u] ", (int) getpid());
802 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200803 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000804 va_start(va, fmt);
805 vfprintf(tracefile, fmt, va);
806 va_end(va);
807}
808
809static void
810trace_vprintf(const char *fmt, va_list va)
811{
812 if (debug != 1)
813 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000814 if (DEBUG_TIME)
815 fprintf(tracefile, "%u ", (int) time(NULL));
816 if (DEBUG_PID)
817 fprintf(tracefile, "[%u] ", (int) getpid());
818 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200819 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000820 vfprintf(tracefile, fmt, va);
821}
822
823static void
824trace_puts(const char *s)
825{
826 if (debug != 1)
827 return;
828 fputs(s, tracefile);
829}
830
831static void
832trace_puts_quoted(char *s)
833{
834 char *p;
835 char c;
836
837 if (debug != 1)
838 return;
839 putc('"', tracefile);
840 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100841 switch ((unsigned char)*p) {
842 case '\n': c = 'n'; goto backslash;
843 case '\t': c = 't'; goto backslash;
844 case '\r': c = 'r'; goto backslash;
845 case '\"': c = '\"'; goto backslash;
846 case '\\': c = '\\'; goto backslash;
847 case CTLESC: c = 'e'; goto backslash;
848 case CTLVAR: c = 'v'; goto backslash;
849 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
850 case CTLBACKQ: c = 'q'; goto backslash;
851 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000852 backslash:
853 putc('\\', tracefile);
854 putc(c, tracefile);
855 break;
856 default:
857 if (*p >= ' ' && *p <= '~')
858 putc(*p, tracefile);
859 else {
860 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100861 putc((*p >> 6) & 03, tracefile);
862 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000863 putc(*p & 07, tracefile);
864 }
865 break;
866 }
867 }
868 putc('"', tracefile);
869}
870
871static void
872trace_puts_args(char **ap)
873{
874 if (debug != 1)
875 return;
876 if (!*ap)
877 return;
878 while (1) {
879 trace_puts_quoted(*ap);
880 if (!*++ap) {
881 putc('\n', tracefile);
882 break;
883 }
884 putc(' ', tracefile);
885 }
886}
887
888static void
889opentrace(void)
890{
891 char s[100];
892#ifdef O_APPEND
893 int flags;
894#endif
895
896 if (debug != 1) {
897 if (tracefile)
898 fflush(tracefile);
899 /* leave open because libedit might be using it */
900 return;
901 }
902 strcpy(s, "./trace");
903 if (tracefile) {
904 if (!freopen(s, "a", tracefile)) {
905 fprintf(stderr, "Can't re-open %s\n", s);
906 debug = 0;
907 return;
908 }
909 } else {
910 tracefile = fopen(s, "a");
911 if (tracefile == NULL) {
912 fprintf(stderr, "Can't open %s\n", s);
913 debug = 0;
914 return;
915 }
916 }
917#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000918 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000919 if (flags >= 0)
920 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
921#endif
922 setlinebuf(tracefile);
923 fputs("\nTracing started.\n", tracefile);
924}
925
926static void
927indent(int amount, char *pfx, FILE *fp)
928{
929 int i;
930
931 for (i = 0; i < amount; i++) {
932 if (pfx && i == amount - 1)
933 fputs(pfx, fp);
934 putc('\t', fp);
935 }
936}
937
938/* little circular references here... */
939static void shtree(union node *n, int ind, char *pfx, FILE *fp);
940
941static void
942sharg(union node *arg, FILE *fp)
943{
944 char *p;
945 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100946 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000947
948 if (arg->type != NARG) {
949 out1fmt("<node type %d>\n", arg->type);
950 abort();
951 }
952 bqlist = arg->narg.backquote;
953 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100954 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000955 case CTLESC:
956 putc(*++p, fp);
957 break;
958 case CTLVAR:
959 putc('$', fp);
960 putc('{', fp);
961 subtype = *++p;
962 if (subtype == VSLENGTH)
963 putc('#', fp);
964
965 while (*p != '=')
966 putc(*p++, fp);
967
968 if (subtype & VSNUL)
969 putc(':', fp);
970
971 switch (subtype & VSTYPE) {
972 case VSNORMAL:
973 putc('}', fp);
974 break;
975 case VSMINUS:
976 putc('-', fp);
977 break;
978 case VSPLUS:
979 putc('+', fp);
980 break;
981 case VSQUESTION:
982 putc('?', fp);
983 break;
984 case VSASSIGN:
985 putc('=', fp);
986 break;
987 case VSTRIMLEFT:
988 putc('#', fp);
989 break;
990 case VSTRIMLEFTMAX:
991 putc('#', fp);
992 putc('#', fp);
993 break;
994 case VSTRIMRIGHT:
995 putc('%', fp);
996 break;
997 case VSTRIMRIGHTMAX:
998 putc('%', fp);
999 putc('%', fp);
1000 break;
1001 case VSLENGTH:
1002 break;
1003 default:
1004 out1fmt("<subtype %d>", subtype);
1005 }
1006 break;
1007 case CTLENDVAR:
1008 putc('}', fp);
1009 break;
1010 case CTLBACKQ:
1011 case CTLBACKQ|CTLQUOTE:
1012 putc('$', fp);
1013 putc('(', fp);
1014 shtree(bqlist->n, -1, NULL, fp);
1015 putc(')', fp);
1016 break;
1017 default:
1018 putc(*p, fp);
1019 break;
1020 }
1021 }
1022}
1023
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001024static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001025shcmd(union node *cmd, FILE *fp)
1026{
1027 union node *np;
1028 int first;
1029 const char *s;
1030 int dftfd;
1031
1032 first = 1;
1033 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001034 if (!first)
1035 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001036 sharg(np, fp);
1037 first = 0;
1038 }
1039 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001040 if (!first)
1041 putc(' ', fp);
1042 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001043 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001044 case NTO: s = ">>"+1; dftfd = 1; break;
1045 case NCLOBBER: s = ">|"; dftfd = 1; break;
1046 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001047#if ENABLE_ASH_BASH_COMPAT
1048 case NTO2:
1049#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001050 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001051 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001052 case NFROMFD: s = "<&"; break;
1053 case NFROMTO: s = "<>"; break;
1054 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001055 }
1056 if (np->nfile.fd != dftfd)
1057 fprintf(fp, "%d", np->nfile.fd);
1058 fputs(s, fp);
1059 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1060 fprintf(fp, "%d", np->ndup.dupfd);
1061 } else {
1062 sharg(np->nfile.fname, fp);
1063 }
1064 first = 0;
1065 }
1066}
1067
1068static void
1069shtree(union node *n, int ind, char *pfx, FILE *fp)
1070{
1071 struct nodelist *lp;
1072 const char *s;
1073
1074 if (n == NULL)
1075 return;
1076
1077 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001078
1079 if (n == NODE_EOF) {
1080 fputs("<EOF>", fp);
1081 return;
1082 }
1083
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001084 switch (n->type) {
1085 case NSEMI:
1086 s = "; ";
1087 goto binop;
1088 case NAND:
1089 s = " && ";
1090 goto binop;
1091 case NOR:
1092 s = " || ";
1093 binop:
1094 shtree(n->nbinary.ch1, ind, NULL, fp);
1095 /* if (ind < 0) */
1096 fputs(s, fp);
1097 shtree(n->nbinary.ch2, ind, NULL, fp);
1098 break;
1099 case NCMD:
1100 shcmd(n, fp);
1101 if (ind >= 0)
1102 putc('\n', fp);
1103 break;
1104 case NPIPE:
1105 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001106 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001107 if (lp->next)
1108 fputs(" | ", fp);
1109 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001110 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001111 fputs(" &", fp);
1112 if (ind >= 0)
1113 putc('\n', fp);
1114 break;
1115 default:
1116 fprintf(fp, "<node type %d>", n->type);
1117 if (ind >= 0)
1118 putc('\n', fp);
1119 break;
1120 }
1121}
1122
1123static void
1124showtree(union node *n)
1125{
1126 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001127 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001128}
1129
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001130#endif /* DEBUG */
1131
1132
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001133/* ============ Parser data */
1134
1135/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001136 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1137 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001138struct strlist {
1139 struct strlist *next;
1140 char *text;
1141};
1142
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001143struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001144
Denis Vlasenkob012b102007-02-19 22:43:01 +00001145struct strpush {
1146 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001147 char *prev_string;
1148 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001149#if ENABLE_ASH_ALIAS
1150 struct alias *ap; /* if push was associated with an alias */
1151#endif
1152 char *string; /* remember the string since it may change */
1153};
1154
1155struct parsefile {
1156 struct parsefile *prev; /* preceding file on stack */
1157 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001158 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001159 int left_in_line; /* number of chars left in this line */
1160 int left_in_buffer; /* number of chars left in this buffer past the line */
1161 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001162 char *buf; /* input buffer */
1163 struct strpush *strpush; /* for pushing strings at this level */
1164 struct strpush basestrpush; /* so pushing one is fast */
1165};
1166
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001167static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001168static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001169static int startlinno; /* line # where last token started */
1170static char *commandname; /* currently executing command */
1171static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001172static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001173
1174
1175/* ============ Message printing */
1176
1177static void
1178ash_vmsg(const char *msg, va_list ap)
1179{
1180 fprintf(stderr, "%s: ", arg0);
1181 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001182 if (strcmp(arg0, commandname))
1183 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001184 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001185 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001186 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001187 vfprintf(stderr, msg, ap);
1188 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001189}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001190
1191/*
1192 * Exverror is called to raise the error exception. If the second argument
1193 * is not NULL then error prints an error message using printf style
1194 * formatting. It then raises the error exception.
1195 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001196static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001197static void
1198ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001199{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001200#if DEBUG
1201 if (msg) {
1202 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1203 TRACEV((msg, ap));
1204 TRACE(("\") pid=%d\n", getpid()));
1205 } else
1206 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1207 if (msg)
1208#endif
1209 ash_vmsg(msg, ap);
1210
1211 flush_stdout_stderr();
1212 raise_exception(cond);
1213 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001214}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001215
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001216static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001217static void
1218ash_msg_and_raise_error(const char *msg, ...)
1219{
1220 va_list ap;
1221
1222 va_start(ap, msg);
1223 ash_vmsg_and_raise(EXERROR, msg, ap);
1224 /* NOTREACHED */
1225 va_end(ap);
1226}
1227
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001228static void raise_error_syntax(const char *) NORETURN;
1229static void
1230raise_error_syntax(const char *msg)
1231{
1232 ash_msg_and_raise_error("syntax error: %s", msg);
1233 /* NOTREACHED */
1234}
1235
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001236static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001237static void
1238ash_msg_and_raise(int cond, const char *msg, ...)
1239{
1240 va_list ap;
1241
1242 va_start(ap, msg);
1243 ash_vmsg_and_raise(cond, msg, ap);
1244 /* NOTREACHED */
1245 va_end(ap);
1246}
1247
1248/*
1249 * error/warning routines for external builtins
1250 */
1251static void
1252ash_msg(const char *fmt, ...)
1253{
1254 va_list ap;
1255
1256 va_start(ap, fmt);
1257 ash_vmsg(fmt, ap);
1258 va_end(ap);
1259}
1260
1261/*
1262 * Return a string describing an error. The returned string may be a
1263 * pointer to a static buffer that will be overwritten on the next call.
1264 * Action describes the operation that got the error.
1265 */
1266static const char *
1267errmsg(int e, const char *em)
1268{
1269 if (e == ENOENT || e == ENOTDIR) {
1270 return em;
1271 }
1272 return strerror(e);
1273}
1274
1275
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001276/* ============ Memory allocation */
1277
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001278#if 0
1279/* I consider these wrappers nearly useless:
1280 * ok, they return you to nearest exception handler, but
1281 * how much memory do you leak in the process, making
1282 * memory starvation worse?
1283 */
1284static void *
1285ckrealloc(void * p, size_t nbytes)
1286{
1287 p = realloc(p, nbytes);
1288 if (!p)
1289 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1290 return p;
1291}
1292
1293static void *
1294ckmalloc(size_t nbytes)
1295{
1296 return ckrealloc(NULL, nbytes);
1297}
1298
1299static void *
1300ckzalloc(size_t nbytes)
1301{
1302 return memset(ckmalloc(nbytes), 0, nbytes);
1303}
1304
1305static char *
1306ckstrdup(const char *s)
1307{
1308 char *p = strdup(s);
1309 if (!p)
1310 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1311 return p;
1312}
1313#else
1314/* Using bbox equivalents. They exit if out of memory */
1315# define ckrealloc xrealloc
1316# define ckmalloc xmalloc
1317# define ckzalloc xzalloc
1318# define ckstrdup xstrdup
1319#endif
1320
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001321/*
1322 * It appears that grabstackstr() will barf with such alignments
1323 * because stalloc() will return a string allocated in a new stackblock.
1324 */
1325#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1326enum {
1327 /* Most machines require the value returned from malloc to be aligned
1328 * in some way. The following macro will get this right
1329 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001330 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001332 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001333};
1334
1335struct stack_block {
1336 struct stack_block *prev;
1337 char space[MINSIZE];
1338};
1339
1340struct stackmark {
1341 struct stack_block *stackp;
1342 char *stacknxt;
1343 size_t stacknleft;
1344 struct stackmark *marknext;
1345};
1346
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347
Denis Vlasenko01631112007-12-16 17:20:38 +00001348struct globals_memstack {
1349 struct stack_block *g_stackp; // = &stackbase;
1350 struct stackmark *markp;
1351 char *g_stacknxt; // = stackbase.space;
1352 char *sstrend; // = stackbase.space + MINSIZE;
1353 size_t g_stacknleft; // = MINSIZE;
1354 int herefd; // = -1;
1355 struct stack_block stackbase;
1356};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001357extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1358#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001359#define g_stackp (G_memstack.g_stackp )
1360#define markp (G_memstack.markp )
1361#define g_stacknxt (G_memstack.g_stacknxt )
1362#define sstrend (G_memstack.sstrend )
1363#define g_stacknleft (G_memstack.g_stacknleft)
1364#define herefd (G_memstack.herefd )
1365#define stackbase (G_memstack.stackbase )
1366#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001367 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1368 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001369 g_stackp = &stackbase; \
1370 g_stacknxt = stackbase.space; \
1371 g_stacknleft = MINSIZE; \
1372 sstrend = stackbase.space + MINSIZE; \
1373 herefd = -1; \
1374} while (0)
1375
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001376
Denis Vlasenko01631112007-12-16 17:20:38 +00001377#define stackblock() ((void *)g_stacknxt)
1378#define stackblocksize() g_stacknleft
1379
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001380/*
1381 * Parse trees for commands are allocated in lifo order, so we use a stack
1382 * to make this more efficient, and also to avoid all sorts of exception
1383 * handling code to handle interrupts in the middle of a parse.
1384 *
1385 * The size 504 was chosen because the Ultrix malloc handles that size
1386 * well.
1387 */
1388static void *
1389stalloc(size_t nbytes)
1390{
1391 char *p;
1392 size_t aligned;
1393
1394 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001395 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001396 size_t len;
1397 size_t blocksize;
1398 struct stack_block *sp;
1399
1400 blocksize = aligned;
1401 if (blocksize < MINSIZE)
1402 blocksize = MINSIZE;
1403 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1404 if (len < blocksize)
1405 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1406 INT_OFF;
1407 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001408 sp->prev = g_stackp;
1409 g_stacknxt = sp->space;
1410 g_stacknleft = blocksize;
1411 sstrend = g_stacknxt + blocksize;
1412 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001413 INT_ON;
1414 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001415 p = g_stacknxt;
1416 g_stacknxt += aligned;
1417 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001418 return p;
1419}
1420
Denis Vlasenko597906c2008-02-20 16:38:54 +00001421static void *
1422stzalloc(size_t nbytes)
1423{
1424 return memset(stalloc(nbytes), 0, nbytes);
1425}
1426
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001427static void
1428stunalloc(void *p)
1429{
1430#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001431 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001432 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001433 abort();
1434 }
1435#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001436 g_stacknleft += g_stacknxt - (char *)p;
1437 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001438}
1439
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001440/*
1441 * Like strdup but works with the ash stack.
1442 */
1443static char *
1444ststrdup(const char *p)
1445{
1446 size_t len = strlen(p) + 1;
1447 return memcpy(stalloc(len), p, len);
1448}
1449
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001450static void
1451setstackmark(struct stackmark *mark)
1452{
Denis Vlasenko01631112007-12-16 17:20:38 +00001453 mark->stackp = g_stackp;
1454 mark->stacknxt = g_stacknxt;
1455 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001456 mark->marknext = markp;
1457 markp = mark;
1458}
1459
1460static void
1461popstackmark(struct stackmark *mark)
1462{
1463 struct stack_block *sp;
1464
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001465 if (!mark->stackp)
1466 return;
1467
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001468 INT_OFF;
1469 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001470 while (g_stackp != mark->stackp) {
1471 sp = g_stackp;
1472 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001473 free(sp);
1474 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001475 g_stacknxt = mark->stacknxt;
1476 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001477 sstrend = mark->stacknxt + mark->stacknleft;
1478 INT_ON;
1479}
1480
1481/*
1482 * When the parser reads in a string, it wants to stick the string on the
1483 * stack and only adjust the stack pointer when it knows how big the
1484 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1485 * of space on top of the stack and stackblocklen returns the length of
1486 * this block. Growstackblock will grow this space by at least one byte,
1487 * possibly moving it (like realloc). Grabstackblock actually allocates the
1488 * part of the block that has been used.
1489 */
1490static void
1491growstackblock(void)
1492{
1493 size_t newlen;
1494
Denis Vlasenko01631112007-12-16 17:20:38 +00001495 newlen = g_stacknleft * 2;
1496 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001497 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1498 if (newlen < 128)
1499 newlen += 128;
1500
Denis Vlasenko01631112007-12-16 17:20:38 +00001501 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001502 struct stack_block *oldstackp;
1503 struct stackmark *xmark;
1504 struct stack_block *sp;
1505 struct stack_block *prevstackp;
1506 size_t grosslen;
1507
1508 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001509 oldstackp = g_stackp;
1510 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001511 prevstackp = sp->prev;
1512 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1513 sp = ckrealloc(sp, grosslen);
1514 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001515 g_stackp = sp;
1516 g_stacknxt = sp->space;
1517 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001518 sstrend = sp->space + newlen;
1519
1520 /*
1521 * Stack marks pointing to the start of the old block
1522 * must be relocated to point to the new block
1523 */
1524 xmark = markp;
1525 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001526 xmark->stackp = g_stackp;
1527 xmark->stacknxt = g_stacknxt;
1528 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001529 xmark = xmark->marknext;
1530 }
1531 INT_ON;
1532 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001533 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001534 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001535 char *p = stalloc(newlen);
1536
1537 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001538 g_stacknxt = memcpy(p, oldspace, oldlen);
1539 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001540 }
1541}
1542
1543static void
1544grabstackblock(size_t len)
1545{
1546 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001547 g_stacknxt += len;
1548 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001549}
1550
1551/*
1552 * The following routines are somewhat easier to use than the above.
1553 * The user declares a variable of type STACKSTR, which may be declared
1554 * to be a register. The macro STARTSTACKSTR initializes things. Then
1555 * the user uses the macro STPUTC to add characters to the string. In
1556 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1557 * grown as necessary. When the user is done, she can just leave the
1558 * string there and refer to it using stackblock(). Or she can allocate
1559 * the space for it using grabstackstr(). If it is necessary to allow
1560 * someone else to use the stack temporarily and then continue to grow
1561 * the string, the user should use grabstack to allocate the space, and
1562 * then call ungrabstr(p) to return to the previous mode of operation.
1563 *
1564 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1565 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1566 * is space for at least one character.
1567 */
1568static void *
1569growstackstr(void)
1570{
1571 size_t len = stackblocksize();
1572 if (herefd >= 0 && len >= 1024) {
1573 full_write(herefd, stackblock(), len);
1574 return stackblock();
1575 }
1576 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001577 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001578}
1579
1580/*
1581 * Called from CHECKSTRSPACE.
1582 */
1583static char *
1584makestrspace(size_t newlen, char *p)
1585{
Denis Vlasenko01631112007-12-16 17:20:38 +00001586 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001587 size_t size = stackblocksize();
1588
1589 for (;;) {
1590 size_t nleft;
1591
1592 size = stackblocksize();
1593 nleft = size - len;
1594 if (nleft >= newlen)
1595 break;
1596 growstackblock();
1597 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001598 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001599}
1600
1601static char *
1602stack_nputstr(const char *s, size_t n, char *p)
1603{
1604 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001605 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001606 return p;
1607}
1608
1609static char *
1610stack_putstr(const char *s, char *p)
1611{
1612 return stack_nputstr(s, strlen(s), p);
1613}
1614
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001615static char *
1616_STPUTC(int c, char *p)
1617{
1618 if (p == sstrend)
1619 p = growstackstr();
1620 *p++ = c;
1621 return p;
1622}
1623
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001624#define STARTSTACKSTR(p) ((p) = stackblock())
1625#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001626#define CHECKSTRSPACE(n, p) do { \
1627 char *q = (p); \
1628 size_t l = (n); \
1629 size_t m = sstrend - q; \
1630 if (l > m) \
1631 (p) = makestrspace(l, q); \
1632} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001633#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001634#define STACKSTRNUL(p) do { \
1635 if ((p) == sstrend) \
1636 (p) = growstackstr(); \
1637 *(p) = '\0'; \
1638} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001639#define STUNPUTC(p) (--(p))
1640#define STTOPC(p) ((p)[-1])
1641#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001642
1643#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001644#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001645#define stackstrend() ((void *)sstrend)
1646
1647
1648/* ============ String helpers */
1649
1650/*
1651 * prefix -- see if pfx is a prefix of string.
1652 */
1653static char *
1654prefix(const char *string, const char *pfx)
1655{
1656 while (*pfx) {
1657 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001658 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001659 }
1660 return (char *) string;
1661}
1662
1663/*
1664 * Check for a valid number. This should be elsewhere.
1665 */
1666static int
1667is_number(const char *p)
1668{
1669 do {
1670 if (!isdigit(*p))
1671 return 0;
1672 } while (*++p != '\0');
1673 return 1;
1674}
1675
1676/*
1677 * Convert a string of digits to an integer, printing an error message on
1678 * failure.
1679 */
1680static int
1681number(const char *s)
1682{
1683 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001684 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001685 return atoi(s);
1686}
1687
1688/*
1689 * Produce a possibly single quoted string suitable as input to the shell.
1690 * The return string is allocated on the stack.
1691 */
1692static char *
1693single_quote(const char *s)
1694{
1695 char *p;
1696
1697 STARTSTACKSTR(p);
1698
1699 do {
1700 char *q;
1701 size_t len;
1702
1703 len = strchrnul(s, '\'') - s;
1704
1705 q = p = makestrspace(len + 3, p);
1706
1707 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001708 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001709 *q++ = '\'';
1710 s += len;
1711
1712 STADJUST(q - p, p);
1713
Denys Vlasenkocd716832009-11-28 22:14:02 +01001714 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001716 len = 0;
1717 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001718
1719 q = p = makestrspace(len + 3, p);
1720
1721 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001722 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001724
1725 STADJUST(q - p, p);
1726 } while (*s);
1727
Denys Vlasenkocd716832009-11-28 22:14:02 +01001728 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729
1730 return stackblock();
1731}
1732
1733
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001734/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735
1736static char **argptr; /* argument list for builtin commands */
1737static char *optionarg; /* set by nextopt (like getopt) */
1738static char *optptr; /* used by nextopt */
1739
1740/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001741 * XXX - should get rid of. Have all builtins use getopt(3).
1742 * The library getopt must have the BSD extension static variable
1743 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001745 * Standard option processing (a la getopt) for builtin routines.
1746 * The only argument that is passed to nextopt is the option string;
1747 * the other arguments are unnecessary. It returns the character,
1748 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749 */
1750static int
1751nextopt(const char *optstring)
1752{
1753 char *p;
1754 const char *q;
1755 char c;
1756
1757 p = optptr;
1758 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001759 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001761 if (p == NULL)
1762 return '\0';
1763 if (*p != '-')
1764 return '\0';
1765 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001766 return '\0';
1767 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001768 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001770 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001771 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001772 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001773 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001774 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001775 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001776 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001777 if (*++q == ':')
1778 q++;
1779 }
1780 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001781 if (*p == '\0') {
1782 p = *argptr++;
1783 if (p == NULL)
1784 ash_msg_and_raise_error("no arg for -%c option", c);
1785 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786 optionarg = p;
1787 p = NULL;
1788 }
1789 optptr = p;
1790 return c;
1791}
1792
1793
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001794/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001795
Denis Vlasenko01631112007-12-16 17:20:38 +00001796/*
1797 * The parsefile structure pointed to by the global variable parsefile
1798 * contains information about the current file being read.
1799 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001800struct shparam {
1801 int nparam; /* # of positional parameters (without $0) */
1802#if ENABLE_ASH_GETOPTS
1803 int optind; /* next parameter to be processed by getopts */
1804 int optoff; /* used by getopts */
1805#endif
1806 unsigned char malloced; /* if parameter list dynamically allocated */
1807 char **p; /* parameter list */
1808};
1809
1810/*
1811 * Free the list of positional parameters.
1812 */
1813static void
1814freeparam(volatile struct shparam *param)
1815{
Denis Vlasenko01631112007-12-16 17:20:38 +00001816 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001817 char **ap, **ap1;
1818 ap = ap1 = param->p;
1819 while (*ap)
1820 free(*ap++);
1821 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001822 }
1823}
1824
1825#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001826static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001827#endif
1828
1829struct var {
1830 struct var *next; /* next entry in hash list */
1831 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001832 const char *var_text; /* name=value */
1833 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001834 /* the variable gets set/unset */
1835};
1836
1837struct localvar {
1838 struct localvar *next; /* next local variable in list */
1839 struct var *vp; /* the variable that was made local */
1840 int flags; /* saved flags */
1841 const char *text; /* saved text */
1842};
1843
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844/* flags */
1845#define VEXPORT 0x01 /* variable is exported */
1846#define VREADONLY 0x02 /* variable cannot be modified */
1847#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1848#define VTEXTFIXED 0x08 /* text is statically allocated */
1849#define VSTACK 0x10 /* text is allocated on the stack */
1850#define VUNSET 0x20 /* the variable is not set */
1851#define VNOFUNC 0x40 /* don't call the callback function */
1852#define VNOSET 0x80 /* do not set variable - just readonly test */
1853#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001854#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001855# define VDYNAMIC 0x200 /* dynamic variable */
1856#else
1857# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001858#endif
1859
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001860
Denis Vlasenko01631112007-12-16 17:20:38 +00001861/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001862#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001863static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001864change_lc_all(const char *value)
1865{
1866 if (value && *value != '\0')
1867 setlocale(LC_ALL, value);
1868}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001869static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001870change_lc_ctype(const char *value)
1871{
1872 if (value && *value != '\0')
1873 setlocale(LC_CTYPE, value);
1874}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001875#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001876#if ENABLE_ASH_MAIL
1877static void chkmail(void);
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001878static void changemail(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001879#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001880static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001881#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001882static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001883#endif
1884
Denis Vlasenko01631112007-12-16 17:20:38 +00001885static const struct {
1886 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001887 const char *var_text;
1888 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001889} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001890 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001892 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1893 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001894#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001895 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1896 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1897 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1898 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001899#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001900 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001901#endif
1902#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001903 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001904#endif
1905#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001906 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1907 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001908#endif
1909#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001910 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
1912};
1913
Denis Vlasenko0b769642008-07-24 07:54:57 +00001914struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001915
1916struct globals_var {
1917 struct shparam shellparam; /* $@ current positional parameters */
1918 struct redirtab *redirlist;
1919 int g_nullredirs;
1920 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1921 struct var *vartab[VTABSIZE];
1922 struct var varinit[ARRAY_SIZE(varinit_data)];
1923};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001924extern struct globals_var *const ash_ptr_to_globals_var;
1925#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001926#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001927//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001928#define g_nullredirs (G_var.g_nullredirs )
1929#define preverrout_fd (G_var.preverrout_fd)
1930#define vartab (G_var.vartab )
1931#define varinit (G_var.varinit )
1932#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001933 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001934 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1935 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001936 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001937 varinit[i].flags = varinit_data[i].flags; \
1938 varinit[i].var_text = varinit_data[i].var_text; \
1939 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001940 } \
1941} while (0)
1942
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001943#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001944#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001945# define vmail (&vifs)[1]
1946# define vmpath (&vmail)[1]
1947# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001948#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001949# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001950#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001951#define vps1 (&vpath)[1]
1952#define vps2 (&vps1)[1]
1953#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001954#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001955# define voptind (&vps4)[1]
1956# if ENABLE_ASH_RANDOM_SUPPORT
1957# define vrandom (&voptind)[1]
1958# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001959#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001960# if ENABLE_ASH_RANDOM_SUPPORT
1961# define vrandom (&vps4)[1]
1962# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001963#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964
1965/*
1966 * The following macros access the values of the above variables.
1967 * They have to skip over the name. They return the null string
1968 * for unset variables.
1969 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001970#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001971#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001972#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001973# define mailval() (vmail.var_text + 5)
1974# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001975# define mpathset() ((vmpath.flags & VUNSET) == 0)
1976#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001977#define pathval() (vpath.var_text + 5)
1978#define ps1val() (vps1.var_text + 4)
1979#define ps2val() (vps2.var_text + 4)
1980#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001981#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001982# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001983#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001985
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001986#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1987#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1988
Denis Vlasenko01631112007-12-16 17:20:38 +00001989#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001990static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001991getoptsreset(const char *value)
1992{
1993 shellparam.optind = number(value);
1994 shellparam.optoff = -1;
1995}
1996#endif
1997
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001998/*
1999 * Return of a legal variable name (a letter or underscore followed by zero or
2000 * more letters, underscores, and digits).
2001 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002002static char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002003endofname(const char *name)
2004{
2005 char *p;
2006
2007 p = (char *) name;
2008 if (!is_name(*p))
2009 return p;
2010 while (*++p) {
2011 if (!is_in_name(*p))
2012 break;
2013 }
2014 return p;
2015}
2016
2017/*
2018 * Compares two strings up to the first = or '\0'. The first
2019 * string must be terminated by '='; the second may be terminated by
2020 * either '=' or '\0'.
2021 */
2022static int
2023varcmp(const char *p, const char *q)
2024{
2025 int c, d;
2026
2027 while ((c = *p) == (d = *q)) {
2028 if (!c || c == '=')
2029 goto out;
2030 p++;
2031 q++;
2032 }
2033 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002034 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002035 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002036 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002037 out:
2038 return c - d;
2039}
2040
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002041/*
2042 * Find the appropriate entry in the hash table from the name.
2043 */
2044static struct var **
2045hashvar(const char *p)
2046{
2047 unsigned hashval;
2048
2049 hashval = ((unsigned char) *p) << 4;
2050 while (*p && *p != '=')
2051 hashval += (unsigned char) *p++;
2052 return &vartab[hashval % VTABSIZE];
2053}
2054
2055static int
2056vpcmp(const void *a, const void *b)
2057{
2058 return varcmp(*(const char **)a, *(const char **)b);
2059}
2060
2061/*
2062 * This routine initializes the builtin variables.
2063 */
2064static void
2065initvar(void)
2066{
2067 struct var *vp;
2068 struct var *end;
2069 struct var **vpp;
2070
2071 /*
2072 * PS1 depends on uid
2073 */
2074#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002075 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002076#else
2077 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002078 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079#endif
2080 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002081 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002082 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002083 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002084 vp->next = *vpp;
2085 *vpp = vp;
2086 } while (++vp < end);
2087}
2088
2089static struct var **
2090findvar(struct var **vpp, const char *name)
2091{
2092 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002093 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094 break;
2095 }
2096 }
2097 return vpp;
2098}
2099
2100/*
2101 * Find the value of a variable. Returns NULL if not set.
2102 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002103static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002104lookupvar(const char *name)
2105{
2106 struct var *v;
2107
2108 v = *findvar(hashvar(name), name);
2109 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002110#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002111 /*
2112 * Dynamic variables are implemented roughly the same way they are
2113 * in bash. Namely, they're "special" so long as they aren't unset.
2114 * As soon as they're unset, they're no longer dynamic, and dynamic
2115 * lookup will no longer happen at that point. -- PFM.
2116 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002117 if (v->flags & VDYNAMIC)
2118 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119#endif
2120 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002121 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002122 }
2123 return NULL;
2124}
2125
2126/*
2127 * Search the environment of a builtin command.
2128 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002129static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002130bltinlookup(const char *name)
2131{
2132 struct strlist *sp;
2133
2134 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002135 if (varcmp(sp->text, name) == 0)
2136 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002137 }
2138 return lookupvar(name);
2139}
2140
2141/*
2142 * Same as setvar except that the variable and value are passed in
2143 * the first argument as name=value. Since the first argument will
2144 * be actually stored in the table, it should not be a string that
2145 * will go away.
2146 * Called with interrupts off.
2147 */
2148static void
2149setvareq(char *s, int flags)
2150{
2151 struct var *vp, **vpp;
2152
2153 vpp = hashvar(s);
2154 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2155 vp = *findvar(vpp, s);
2156 if (vp) {
2157 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2158 const char *n;
2159
2160 if (flags & VNOSAVE)
2161 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002162 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002163 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2164 }
2165
2166 if (flags & VNOSET)
2167 return;
2168
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002169 if (vp->var_func && !(flags & VNOFUNC))
2170 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002171
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002172 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2173 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002174
2175 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2176 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002177 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002178 if (flags & VNOSET)
2179 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002180 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002181 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002182 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002183 *vpp = vp;
2184 }
2185 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2186 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002187 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002188 vp->flags = flags;
2189}
2190
2191/*
2192 * Set the value of a variable. The flags argument is ored with the
2193 * flags of the variable. If val is NULL, the variable is unset.
2194 */
2195static void
2196setvar(const char *name, const char *val, int flags)
2197{
2198 char *p, *q;
2199 size_t namelen;
2200 char *nameeq;
2201 size_t vallen;
2202
2203 q = endofname(name);
2204 p = strchrnul(q, '=');
2205 namelen = p - name;
2206 if (!namelen || p != q)
2207 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2208 vallen = 0;
2209 if (val == NULL) {
2210 flags |= VUNSET;
2211 } else {
2212 vallen = strlen(val);
2213 }
2214 INT_OFF;
2215 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002216 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002217 if (val) {
2218 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002219 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002220 }
2221 *p = '\0';
2222 setvareq(nameeq, flags | VNOSAVE);
2223 INT_ON;
2224}
2225
Denys Vlasenko03dad222010-01-12 23:29:57 +01002226static void FAST_FUNC
2227setvar2(const char *name, const char *val)
2228{
2229 setvar(name, val, 0);
2230}
2231
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002232#if ENABLE_ASH_GETOPTS
2233/*
2234 * Safe version of setvar, returns 1 on success 0 on failure.
2235 */
2236static int
2237setvarsafe(const char *name, const char *val, int flags)
2238{
2239 int err;
2240 volatile int saveint;
2241 struct jmploc *volatile savehandler = exception_handler;
2242 struct jmploc jmploc;
2243
2244 SAVE_INT(saveint);
2245 if (setjmp(jmploc.loc))
2246 err = 1;
2247 else {
2248 exception_handler = &jmploc;
2249 setvar(name, val, flags);
2250 err = 0;
2251 }
2252 exception_handler = savehandler;
2253 RESTORE_INT(saveint);
2254 return err;
2255}
2256#endif
2257
2258/*
2259 * Unset the specified variable.
2260 */
2261static int
2262unsetvar(const char *s)
2263{
2264 struct var **vpp;
2265 struct var *vp;
2266 int retval;
2267
2268 vpp = findvar(hashvar(s), s);
2269 vp = *vpp;
2270 retval = 2;
2271 if (vp) {
2272 int flags = vp->flags;
2273
2274 retval = 1;
2275 if (flags & VREADONLY)
2276 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002277#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002278 vp->flags &= ~VDYNAMIC;
2279#endif
2280 if (flags & VUNSET)
2281 goto ok;
2282 if ((flags & VSTRFIXED) == 0) {
2283 INT_OFF;
2284 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002285 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002286 *vpp = vp->next;
2287 free(vp);
2288 INT_ON;
2289 } else {
2290 setvar(s, 0, 0);
2291 vp->flags &= ~VEXPORT;
2292 }
2293 ok:
2294 retval = 0;
2295 }
2296 out:
2297 return retval;
2298}
2299
2300/*
2301 * Process a linked list of variable assignments.
2302 */
2303static void
2304listsetvar(struct strlist *list_set_var, int flags)
2305{
2306 struct strlist *lp = list_set_var;
2307
2308 if (!lp)
2309 return;
2310 INT_OFF;
2311 do {
2312 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002313 lp = lp->next;
2314 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002315 INT_ON;
2316}
2317
2318/*
2319 * Generate a list of variables satisfying the given conditions.
2320 */
2321static char **
2322listvars(int on, int off, char ***end)
2323{
2324 struct var **vpp;
2325 struct var *vp;
2326 char **ep;
2327 int mask;
2328
2329 STARTSTACKSTR(ep);
2330 vpp = vartab;
2331 mask = on | off;
2332 do {
2333 for (vp = *vpp; vp; vp = vp->next) {
2334 if ((vp->flags & mask) == on) {
2335 if (ep == stackstrend())
2336 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002337 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002338 }
2339 }
2340 } while (++vpp < vartab + VTABSIZE);
2341 if (ep == stackstrend())
2342 ep = growstackstr();
2343 if (end)
2344 *end = ep;
2345 *ep++ = NULL;
2346 return grabstackstr(ep);
2347}
2348
2349
2350/* ============ Path search helper
2351 *
2352 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002353 * of the path before the first call; path_advance will update
2354 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002355 * the possible path expansions in sequence. If an option (indicated by
2356 * a percent sign) appears in the path entry then the global variable
2357 * pathopt will be set to point to it; otherwise pathopt will be set to
2358 * NULL.
2359 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002360static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002361
2362static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002363path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002364{
2365 const char *p;
2366 char *q;
2367 const char *start;
2368 size_t len;
2369
2370 if (*path == NULL)
2371 return NULL;
2372 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002373 for (p = start; *p && *p != ':' && *p != '%'; p++)
2374 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002375 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2376 while (stackblocksize() < len)
2377 growstackblock();
2378 q = stackblock();
2379 if (p != start) {
2380 memcpy(q, start, p - start);
2381 q += p - start;
2382 *q++ = '/';
2383 }
2384 strcpy(q, name);
2385 pathopt = NULL;
2386 if (*p == '%') {
2387 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002388 while (*p && *p != ':')
2389 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002390 }
2391 if (*p == ':')
2392 *path = p + 1;
2393 else
2394 *path = NULL;
2395 return stalloc(len);
2396}
2397
2398
2399/* ============ Prompt */
2400
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002401static smallint doprompt; /* if set, prompt the user */
2402static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002403
2404#if ENABLE_FEATURE_EDITING
2405static line_input_t *line_input_state;
2406static const char *cmdedit_prompt;
2407static void
2408putprompt(const char *s)
2409{
2410 if (ENABLE_ASH_EXPAND_PRMT) {
2411 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002412 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002413 return;
2414 }
2415 cmdedit_prompt = s;
2416}
2417#else
2418static void
2419putprompt(const char *s)
2420{
2421 out2str(s);
2422}
2423#endif
2424
2425#if ENABLE_ASH_EXPAND_PRMT
2426/* expandstr() needs parsing machinery, so it is far away ahead... */
2427static const char *expandstr(const char *ps);
2428#else
2429#define expandstr(s) s
2430#endif
2431
2432static void
2433setprompt(int whichprompt)
2434{
2435 const char *prompt;
2436#if ENABLE_ASH_EXPAND_PRMT
2437 struct stackmark smark;
2438#endif
2439
2440 needprompt = 0;
2441
2442 switch (whichprompt) {
2443 case 1:
2444 prompt = ps1val();
2445 break;
2446 case 2:
2447 prompt = ps2val();
2448 break;
2449 default: /* 0 */
2450 prompt = nullstr;
2451 }
2452#if ENABLE_ASH_EXPAND_PRMT
2453 setstackmark(&smark);
2454 stalloc(stackblocksize());
2455#endif
2456 putprompt(expandstr(prompt));
2457#if ENABLE_ASH_EXPAND_PRMT
2458 popstackmark(&smark);
2459#endif
2460}
2461
2462
2463/* ============ The cd and pwd commands */
2464
2465#define CD_PHYSICAL 1
2466#define CD_PRINT 2
2467
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002468static int
2469cdopt(void)
2470{
2471 int flags = 0;
2472 int i, j;
2473
2474 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002475 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002476 if (i != j) {
2477 flags ^= CD_PHYSICAL;
2478 j = i;
2479 }
2480 }
2481
2482 return flags;
2483}
2484
2485/*
2486 * Update curdir (the name of the current directory) in response to a
2487 * cd command.
2488 */
2489static const char *
2490updatepwd(const char *dir)
2491{
2492 char *new;
2493 char *p;
2494 char *cdcomppath;
2495 const char *lim;
2496
2497 cdcomppath = ststrdup(dir);
2498 STARTSTACKSTR(new);
2499 if (*dir != '/') {
2500 if (curdir == nullstr)
2501 return 0;
2502 new = stack_putstr(curdir, new);
2503 }
2504 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002505 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002506 if (*dir != '/') {
2507 if (new[-1] != '/')
2508 USTPUTC('/', new);
2509 if (new > lim && *lim == '/')
2510 lim++;
2511 } else {
2512 USTPUTC('/', new);
2513 cdcomppath++;
2514 if (dir[1] == '/' && dir[2] != '/') {
2515 USTPUTC('/', new);
2516 cdcomppath++;
2517 lim++;
2518 }
2519 }
2520 p = strtok(cdcomppath, "/");
2521 while (p) {
2522 switch (*p) {
2523 case '.':
2524 if (p[1] == '.' && p[2] == '\0') {
2525 while (new > lim) {
2526 STUNPUTC(new);
2527 if (new[-1] == '/')
2528 break;
2529 }
2530 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002531 }
2532 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002533 break;
2534 /* fall through */
2535 default:
2536 new = stack_putstr(p, new);
2537 USTPUTC('/', new);
2538 }
2539 p = strtok(0, "/");
2540 }
2541 if (new > lim)
2542 STUNPUTC(new);
2543 *new = 0;
2544 return stackblock();
2545}
2546
2547/*
2548 * Find out what the current directory is. If we already know the current
2549 * directory, this routine returns immediately.
2550 */
2551static char *
2552getpwd(void)
2553{
Denis Vlasenko01631112007-12-16 17:20:38 +00002554 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002555 return dir ? dir : nullstr;
2556}
2557
2558static void
2559setpwd(const char *val, int setold)
2560{
2561 char *oldcur, *dir;
2562
2563 oldcur = dir = curdir;
2564
2565 if (setold) {
2566 setvar("OLDPWD", oldcur, VEXPORT);
2567 }
2568 INT_OFF;
2569 if (physdir != nullstr) {
2570 if (physdir != oldcur)
2571 free(physdir);
2572 physdir = nullstr;
2573 }
2574 if (oldcur == val || !val) {
2575 char *s = getpwd();
2576 physdir = s;
2577 if (!val)
2578 dir = s;
2579 } else
2580 dir = ckstrdup(val);
2581 if (oldcur != dir && oldcur != nullstr) {
2582 free(oldcur);
2583 }
2584 curdir = dir;
2585 INT_ON;
2586 setvar("PWD", dir, VEXPORT);
2587}
2588
2589static void hashcd(void);
2590
2591/*
2592 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2593 * know that the current directory has changed.
2594 */
2595static int
2596docd(const char *dest, int flags)
2597{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002598 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002599 int err;
2600
2601 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2602
2603 INT_OFF;
2604 if (!(flags & CD_PHYSICAL)) {
2605 dir = updatepwd(dest);
2606 if (dir)
2607 dest = dir;
2608 }
2609 err = chdir(dest);
2610 if (err)
2611 goto out;
2612 setpwd(dir, 1);
2613 hashcd();
2614 out:
2615 INT_ON;
2616 return err;
2617}
2618
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002619static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002620cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002621{
2622 const char *dest;
2623 const char *path;
2624 const char *p;
2625 char c;
2626 struct stat statb;
2627 int flags;
2628
2629 flags = cdopt();
2630 dest = *argptr;
2631 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002632 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002633 else if (LONE_DASH(dest)) {
2634 dest = bltinlookup("OLDPWD");
2635 flags |= CD_PRINT;
2636 }
2637 if (!dest)
2638 dest = nullstr;
2639 if (*dest == '/')
2640 goto step7;
2641 if (*dest == '.') {
2642 c = dest[1];
2643 dotdot:
2644 switch (c) {
2645 case '\0':
2646 case '/':
2647 goto step6;
2648 case '.':
2649 c = dest[2];
2650 if (c != '.')
2651 goto dotdot;
2652 }
2653 }
2654 if (!*dest)
2655 dest = ".";
2656 path = bltinlookup("CDPATH");
2657 if (!path) {
2658 step6:
2659 step7:
2660 p = dest;
2661 goto docd;
2662 }
2663 do {
2664 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002665 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002666 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2667 if (c && c != ':')
2668 flags |= CD_PRINT;
2669 docd:
2670 if (!docd(p, flags))
2671 goto out;
2672 break;
2673 }
2674 } while (path);
2675 ash_msg_and_raise_error("can't cd to %s", dest);
2676 /* NOTREACHED */
2677 out:
2678 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002679 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002680 return 0;
2681}
2682
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002683static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002684pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002685{
2686 int flags;
2687 const char *dir = curdir;
2688
2689 flags = cdopt();
2690 if (flags) {
2691 if (physdir == nullstr)
2692 setpwd(dir, 0);
2693 dir = physdir;
2694 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002695 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002696 return 0;
2697}
2698
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002699
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002700/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002701
Denis Vlasenko834dee72008-10-07 09:18:30 +00002702
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002703#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002704
Eric Andersenc470f442003-07-28 09:56:35 +00002705/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002706#define CWORD 0 /* character is nothing special */
2707#define CNL 1 /* newline character */
2708#define CBACK 2 /* a backslash character */
2709#define CSQUOTE 3 /* single quote */
2710#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002711#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002712#define CBQUOTE 6 /* backwards single quote */
2713#define CVAR 7 /* a dollar sign */
2714#define CENDVAR 8 /* a '}' character */
2715#define CLP 9 /* a left paren in arithmetic */
2716#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002717#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002718#define CCTL 12 /* like CWORD, except it must be escaped */
2719#define CSPCL 13 /* these terminate a word */
2720#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002721
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002722#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002723#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002724# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002725#endif
2726
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002727#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002728
Mike Frysinger98c52642009-04-02 10:02:37 +00002729#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002730# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002731#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002732# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002733#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002734static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002735#if ENABLE_ASH_ALIAS
2736 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2737#endif
2738 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2739 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2740 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2741 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2742 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2743 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2744 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2745 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2746 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2747 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2748 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002749#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002750 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2751 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2752 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2753#endif
2754#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002755};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002756/* Constants below must match table above */
2757enum {
2758#if ENABLE_ASH_ALIAS
2759 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2760#endif
2761 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2762 CNL_CNL_CNL_CNL , /* 2 */
2763 CWORD_CCTL_CCTL_CWORD , /* 3 */
2764 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2765 CVAR_CVAR_CWORD_CVAR , /* 5 */
2766 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2767 CSPCL_CWORD_CWORD_CLP , /* 7 */
2768 CSPCL_CWORD_CWORD_CRP , /* 8 */
2769 CBACK_CBACK_CCTL_CBACK , /* 9 */
2770 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2771 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2772 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2773 CWORD_CWORD_CWORD_CWORD , /* 13 */
2774 CCTL_CCTL_CCTL_CCTL , /* 14 */
2775};
Eric Andersen2870d962001-07-02 17:27:21 +00002776
Denys Vlasenkocd716832009-11-28 22:14:02 +01002777/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2778 * caller must ensure proper cast on it if c is *char_ptr!
2779 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002780/* Values for syntax param */
2781#define BASESYNTAX 0 /* not in quotes */
2782#define DQSYNTAX 1 /* in double quotes */
2783#define SQSYNTAX 2 /* in single quotes */
2784#define ARISYNTAX 3 /* in arithmetic */
2785#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002786
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002787#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002788
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002789static int
2790SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002791{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002792 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002793# if ENABLE_ASH_ALIAS
2794 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002795 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2796 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2797 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2798 11, 3 /* "}~" */
2799 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002800# else
2801 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002802 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2803 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2804 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2805 10, 2 /* "}~" */
2806 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002807# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002808 const char *s;
2809 int indx;
2810
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002811 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002812 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002813# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002814 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002815 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002816 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002817# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002818 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002819 /* Cast is purely for paranoia here,
2820 * just in case someone passed signed char to us */
2821 if ((unsigned char)c >= CTL_FIRST
2822 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002823 ) {
2824 return CCTL;
2825 }
2826 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002827 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002828 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002829 indx = syntax_index_table[s - spec_symbls];
2830 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002831 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002832}
2833
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002834#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002835
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002836static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002837 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002838 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2848 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2849 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2871 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2872 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2873 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2875 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2878 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2879 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2880 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2881 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2883 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2884 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2885 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2886 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2897 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2898 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2899 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2900 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2901 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2902 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2930 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2931 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2932 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2935 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2963 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2964 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2965 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2966 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2967 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2968 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2969 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2970 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2971 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2972 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2973 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2974 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2975 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2976 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2977 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2978 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2979 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2980 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2981 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2982 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2983 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2986 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 150 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 151 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 152 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 153 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 154 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 155 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 156 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 157 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 158 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 159 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 160 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003094 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003095# if ENABLE_ASH_ALIAS
3096 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3097# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003098};
3099
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003100# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003101
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003102#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003103
Eric Andersen2870d962001-07-02 17:27:21 +00003104
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003105/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003106
Denis Vlasenko131ae172007-02-18 13:00:19 +00003107#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003108
3109#define ALIASINUSE 1
3110#define ALIASDEAD 2
3111
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003112struct alias {
3113 struct alias *next;
3114 char *name;
3115 char *val;
3116 int flag;
3117};
3118
Denis Vlasenko01631112007-12-16 17:20:38 +00003119
3120static struct alias **atab; // [ATABSIZE];
3121#define INIT_G_alias() do { \
3122 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3123} while (0)
3124
Eric Andersen2870d962001-07-02 17:27:21 +00003125
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003126static struct alias **
3127__lookupalias(const char *name) {
3128 unsigned int hashval;
3129 struct alias **app;
3130 const char *p;
3131 unsigned int ch;
3132
3133 p = name;
3134
3135 ch = (unsigned char)*p;
3136 hashval = ch << 4;
3137 while (ch) {
3138 hashval += ch;
3139 ch = (unsigned char)*++p;
3140 }
3141 app = &atab[hashval % ATABSIZE];
3142
3143 for (; *app; app = &(*app)->next) {
3144 if (strcmp(name, (*app)->name) == 0) {
3145 break;
3146 }
3147 }
3148
3149 return app;
3150}
3151
3152static struct alias *
3153lookupalias(const char *name, int check)
3154{
3155 struct alias *ap = *__lookupalias(name);
3156
3157 if (check && ap && (ap->flag & ALIASINUSE))
3158 return NULL;
3159 return ap;
3160}
3161
3162static struct alias *
3163freealias(struct alias *ap)
3164{
3165 struct alias *next;
3166
3167 if (ap->flag & ALIASINUSE) {
3168 ap->flag |= ALIASDEAD;
3169 return ap;
3170 }
3171
3172 next = ap->next;
3173 free(ap->name);
3174 free(ap->val);
3175 free(ap);
3176 return next;
3177}
Eric Andersencb57d552001-06-28 07:25:16 +00003178
Eric Andersenc470f442003-07-28 09:56:35 +00003179static void
3180setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003181{
3182 struct alias *ap, **app;
3183
3184 app = __lookupalias(name);
3185 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003186 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003187 if (ap) {
3188 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003189 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003190 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003191 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003192 ap->flag &= ~ALIASDEAD;
3193 } else {
3194 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003195 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003196 ap->name = ckstrdup(name);
3197 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003198 /*ap->flag = 0; - ckzalloc did it */
3199 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003200 *app = ap;
3201 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003202 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003203}
3204
Eric Andersenc470f442003-07-28 09:56:35 +00003205static int
3206unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003207{
Eric Andersencb57d552001-06-28 07:25:16 +00003208 struct alias **app;
3209
3210 app = __lookupalias(name);
3211
3212 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003213 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003214 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003215 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003216 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003217 }
3218
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003219 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003220}
3221
Eric Andersenc470f442003-07-28 09:56:35 +00003222static void
3223rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003224{
Eric Andersencb57d552001-06-28 07:25:16 +00003225 struct alias *ap, **app;
3226 int i;
3227
Denis Vlasenkob012b102007-02-19 22:43:01 +00003228 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003229 for (i = 0; i < ATABSIZE; i++) {
3230 app = &atab[i];
3231 for (ap = *app; ap; ap = *app) {
3232 *app = freealias(*app);
3233 if (ap == *app) {
3234 app = &ap->next;
3235 }
3236 }
3237 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003238 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003239}
3240
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003241static void
3242printalias(const struct alias *ap)
3243{
3244 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3245}
3246
Eric Andersencb57d552001-06-28 07:25:16 +00003247/*
3248 * TODO - sort output
3249 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003250static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003251aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003252{
3253 char *n, *v;
3254 int ret = 0;
3255 struct alias *ap;
3256
Denis Vlasenko68404f12008-03-17 09:00:54 +00003257 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003258 int i;
3259
Denis Vlasenko68404f12008-03-17 09:00:54 +00003260 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003261 for (ap = atab[i]; ap; ap = ap->next) {
3262 printalias(ap);
3263 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003264 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003265 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003266 }
3267 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003268 v = strchr(n+1, '=');
3269 if (v == NULL) { /* n+1: funny ksh stuff */
3270 ap = *__lookupalias(n);
3271 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003272 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003273 ret = 1;
3274 } else
3275 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003276 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003277 *v++ = '\0';
3278 setalias(n, v);
3279 }
3280 }
3281
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003282 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003283}
3284
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003285static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003286unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003287{
3288 int i;
3289
3290 while ((i = nextopt("a")) != '\0') {
3291 if (i == 'a') {
3292 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003293 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003294 }
3295 }
3296 for (i = 0; *argptr; argptr++) {
3297 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003298 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003299 i = 1;
3300 }
3301 }
3302
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003303 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003304}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003305
Denis Vlasenko131ae172007-02-18 13:00:19 +00003306#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003307
Eric Andersenc470f442003-07-28 09:56:35 +00003308
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003309/* ============ jobs.c */
3310
3311/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003312#define FORK_FG 0
3313#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003314#define FORK_NOJOB 2
3315
3316/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003317#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3318#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3319#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321/*
3322 * A job structure contains information about a job. A job is either a
3323 * single process or a set of processes contained in a pipeline. In the
3324 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3325 * array of pids.
3326 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003328 pid_t ps_pid; /* process id */
3329 int ps_status; /* last process status from wait() */
3330 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331};
3332
3333struct job {
3334 struct procstat ps0; /* status of process */
3335 struct procstat *ps; /* status or processes when more than one */
3336#if JOBS
3337 int stopstatus; /* status of a stopped job */
3338#endif
3339 uint32_t
3340 nprocs: 16, /* number of processes */
3341 state: 8,
3342#define JOBRUNNING 0 /* at least one proc running */
3343#define JOBSTOPPED 1 /* all procs are stopped */
3344#define JOBDONE 2 /* all procs are completed */
3345#if JOBS
3346 sigint: 1, /* job was killed by SIGINT */
3347 jobctl: 1, /* job running under job control */
3348#endif
3349 waited: 1, /* true if this entry has been waited for */
3350 used: 1, /* true if this entry is in used */
3351 changed: 1; /* true if status has changed */
3352 struct job *prev_job; /* previous job */
3353};
3354
Denis Vlasenko68404f12008-03-17 09:00:54 +00003355static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356static int forkshell(struct job *, union node *, int);
3357static int waitforjob(struct job *);
3358
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003359#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003360enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003361#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003363static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003365#endif
3366
3367/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003368 * Ignore a signal.
3369 */
3370static void
3371ignoresig(int signo)
3372{
3373 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3374 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3375 /* No, need to do it */
3376 signal(signo, SIG_IGN);
3377 }
3378 sigmode[signo - 1] = S_HARD_IGN;
3379}
3380
3381/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003382 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003383 */
3384static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003385signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003386{
3387 gotsig[signo - 1] = 1;
3388
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003389 if (signo == SIGINT && !trap[SIGINT]) {
3390 if (!suppress_int) {
3391 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003392 raise_interrupt(); /* does not return */
3393 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003394 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003395 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003396 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003397 }
3398}
3399
3400/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003401 * Set the signal handler for the specified signal. The routine figures
3402 * out what it should be set to.
3403 */
3404static void
3405setsignal(int signo)
3406{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003407 char *t;
3408 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003409 struct sigaction act;
3410
3411 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003412 new_act = S_DFL;
3413 if (t != NULL) { /* trap for this sig is set */
3414 new_act = S_CATCH;
3415 if (t[0] == '\0') /* trap is "": ignore this sig */
3416 new_act = S_IGN;
3417 }
3418
3419 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003420 switch (signo) {
3421 case SIGINT:
3422 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003423 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003424 break;
3425 case SIGQUIT:
3426#if DEBUG
3427 if (debug)
3428 break;
3429#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003430 /* man bash:
3431 * "In all cases, bash ignores SIGQUIT. Non-builtin
3432 * commands run by bash have signal handlers
3433 * set to the values inherited by the shell
3434 * from its parent". */
3435 new_act = S_IGN;
3436 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003437 case SIGTERM:
3438 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003439 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003440 break;
3441#if JOBS
3442 case SIGTSTP:
3443 case SIGTTOU:
3444 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003445 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003446 break;
3447#endif
3448 }
3449 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003450//TODO: if !rootshell, we reset SIGQUIT to DFL,
3451//whereas we have to restore it to what shell got on entry
3452//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003453
3454 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003455 cur_act = *t;
3456 if (cur_act == 0) {
3457 /* current setting is not yet known */
3458 if (sigaction(signo, NULL, &act)) {
3459 /* pretend it worked; maybe we should give a warning,
3460 * but other shells don't. We don't alter sigmode,
3461 * so we retry every time.
3462 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003463 return;
3464 }
3465 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003466 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003467 if (mflag
3468 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3469 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003470 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003471 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003472 }
3473 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003474 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003475 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003476
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003477 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003478 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003479 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003480 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003481 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3482 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003483 break;
3484 case S_IGN:
3485 act.sa_handler = SIG_IGN;
3486 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003487 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003488 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003489
3490 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003491}
3492
3493/* mode flags for set_curjob */
3494#define CUR_DELETE 2
3495#define CUR_RUNNING 1
3496#define CUR_STOPPED 0
3497
3498/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003499#define DOWAIT_NONBLOCK WNOHANG
3500#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003501
3502#if JOBS
3503/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003504static int initialpgrp; //references:2
3505static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003506#endif
3507/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003508static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003509/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003510static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003511/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003512static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003513/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003514static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003515
3516static void
3517set_curjob(struct job *jp, unsigned mode)
3518{
3519 struct job *jp1;
3520 struct job **jpp, **curp;
3521
3522 /* first remove from list */
3523 jpp = curp = &curjob;
3524 do {
3525 jp1 = *jpp;
3526 if (jp1 == jp)
3527 break;
3528 jpp = &jp1->prev_job;
3529 } while (1);
3530 *jpp = jp1->prev_job;
3531
3532 /* Then re-insert in correct position */
3533 jpp = curp;
3534 switch (mode) {
3535 default:
3536#if DEBUG
3537 abort();
3538#endif
3539 case CUR_DELETE:
3540 /* job being deleted */
3541 break;
3542 case CUR_RUNNING:
3543 /* newly created job or backgrounded job,
3544 put after all stopped jobs. */
3545 do {
3546 jp1 = *jpp;
3547#if JOBS
3548 if (!jp1 || jp1->state != JOBSTOPPED)
3549#endif
3550 break;
3551 jpp = &jp1->prev_job;
3552 } while (1);
3553 /* FALLTHROUGH */
3554#if JOBS
3555 case CUR_STOPPED:
3556#endif
3557 /* newly stopped job - becomes curjob */
3558 jp->prev_job = *jpp;
3559 *jpp = jp;
3560 break;
3561 }
3562}
3563
3564#if JOBS || DEBUG
3565static int
3566jobno(const struct job *jp)
3567{
3568 return jp - jobtab + 1;
3569}
3570#endif
3571
3572/*
3573 * Convert a job name to a job structure.
3574 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003575#if !JOBS
3576#define getjob(name, getctl) getjob(name)
3577#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003578static struct job *
3579getjob(const char *name, int getctl)
3580{
3581 struct job *jp;
3582 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003583 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003584 unsigned num;
3585 int c;
3586 const char *p;
3587 char *(*match)(const char *, const char *);
3588
3589 jp = curjob;
3590 p = name;
3591 if (!p)
3592 goto currentjob;
3593
3594 if (*p != '%')
3595 goto err;
3596
3597 c = *++p;
3598 if (!c)
3599 goto currentjob;
3600
3601 if (!p[1]) {
3602 if (c == '+' || c == '%') {
3603 currentjob:
3604 err_msg = "No current job";
3605 goto check;
3606 }
3607 if (c == '-') {
3608 if (jp)
3609 jp = jp->prev_job;
3610 err_msg = "No previous job";
3611 check:
3612 if (!jp)
3613 goto err;
3614 goto gotit;
3615 }
3616 }
3617
3618 if (is_number(p)) {
3619 num = atoi(p);
3620 if (num < njobs) {
3621 jp = jobtab + num - 1;
3622 if (jp->used)
3623 goto gotit;
3624 goto err;
3625 }
3626 }
3627
3628 match = prefix;
3629 if (*p == '?') {
3630 match = strstr;
3631 p++;
3632 }
3633
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003634 found = NULL;
3635 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003636 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003637 if (found)
3638 goto err;
3639 found = jp;
3640 err_msg = "%s: ambiguous";
3641 }
3642 jp = jp->prev_job;
3643 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003644 if (!found)
3645 goto err;
3646 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003647
3648 gotit:
3649#if JOBS
3650 err_msg = "job %s not created under job control";
3651 if (getctl && jp->jobctl == 0)
3652 goto err;
3653#endif
3654 return jp;
3655 err:
3656 ash_msg_and_raise_error(err_msg, name);
3657}
3658
3659/*
3660 * Mark a job structure as unused.
3661 */
3662static void
3663freejob(struct job *jp)
3664{
3665 struct procstat *ps;
3666 int i;
3667
3668 INT_OFF;
3669 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003670 if (ps->ps_cmd != nullstr)
3671 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003672 }
3673 if (jp->ps != &jp->ps0)
3674 free(jp->ps);
3675 jp->used = 0;
3676 set_curjob(jp, CUR_DELETE);
3677 INT_ON;
3678}
3679
3680#if JOBS
3681static void
3682xtcsetpgrp(int fd, pid_t pgrp)
3683{
3684 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003685 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686}
3687
3688/*
3689 * Turn job control on and off.
3690 *
3691 * Note: This code assumes that the third arg to ioctl is a character
3692 * pointer, which is true on Berkeley systems but not System V. Since
3693 * System V doesn't have job control yet, this isn't a problem now.
3694 *
3695 * Called with interrupts off.
3696 */
3697static void
3698setjobctl(int on)
3699{
3700 int fd;
3701 int pgrp;
3702
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003703 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003704 return;
3705 if (on) {
3706 int ofd;
3707 ofd = fd = open(_PATH_TTY, O_RDWR);
3708 if (fd < 0) {
3709 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3710 * That sometimes helps to acquire controlling tty.
3711 * Obviously, a workaround for bugs when someone
3712 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003713 fd = 2;
3714 while (!isatty(fd))
3715 if (--fd < 0)
3716 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003717 }
3718 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003719 if (ofd >= 0)
3720 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003721 if (fd < 0)
3722 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003723 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003724 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003725 do { /* while we are in the background */
3726 pgrp = tcgetpgrp(fd);
3727 if (pgrp < 0) {
3728 out:
3729 ash_msg("can't access tty; job control turned off");
3730 mflag = on = 0;
3731 goto close;
3732 }
3733 if (pgrp == getpgrp())
3734 break;
3735 killpg(0, SIGTTIN);
3736 } while (1);
3737 initialpgrp = pgrp;
3738
3739 setsignal(SIGTSTP);
3740 setsignal(SIGTTOU);
3741 setsignal(SIGTTIN);
3742 pgrp = rootpid;
3743 setpgid(0, pgrp);
3744 xtcsetpgrp(fd, pgrp);
3745 } else {
3746 /* turning job control off */
3747 fd = ttyfd;
3748 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003749 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003750 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003751 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003752 setpgid(0, pgrp);
3753 setsignal(SIGTSTP);
3754 setsignal(SIGTTOU);
3755 setsignal(SIGTTIN);
3756 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003757 if (fd >= 0)
3758 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003759 fd = -1;
3760 }
3761 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003762 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003763}
3764
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003765static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003766killcmd(int argc, char **argv)
3767{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003768 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003769 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003770 do {
3771 if (argv[i][0] == '%') {
3772 struct job *jp = getjob(argv[i], 0);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003773 unsigned pid = jp->ps[0].ps_pid;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003774 /* Enough space for ' -NNN<nul>' */
3775 argv[i] = alloca(sizeof(int)*3 + 3);
3776 /* kill_main has matching code to expect
3777 * leading space. Needed to not confuse
3778 * negative pids with "kill -SIGNAL_NO" syntax */
3779 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003781 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003782 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003783 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784}
3785
3786static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003787showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003789 struct procstat *ps;
3790 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791
Denys Vlasenko285ad152009-12-04 23:02:27 +01003792 psend = jp->ps + jp->nprocs;
3793 for (ps = jp->ps + 1; ps < psend; ps++)
3794 printf(" | %s", ps->ps_cmd);
3795 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796 flush_stdout_stderr();
3797}
3798
3799
3800static int
3801restartjob(struct job *jp, int mode)
3802{
3803 struct procstat *ps;
3804 int i;
3805 int status;
3806 pid_t pgid;
3807
3808 INT_OFF;
3809 if (jp->state == JOBDONE)
3810 goto out;
3811 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003812 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003813 if (mode == FORK_FG)
3814 xtcsetpgrp(ttyfd, pgid);
3815 killpg(pgid, SIGCONT);
3816 ps = jp->ps;
3817 i = jp->nprocs;
3818 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003819 if (WIFSTOPPED(ps->ps_status)) {
3820 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003821 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003822 ps++;
3823 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003824 out:
3825 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3826 INT_ON;
3827 return status;
3828}
3829
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003830static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003831fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003832{
3833 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003834 int mode;
3835 int retval;
3836
3837 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3838 nextopt(nullstr);
3839 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840 do {
3841 jp = getjob(*argv, 1);
3842 if (mode == FORK_BG) {
3843 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003844 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003845 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003846 out1str(jp->ps[0].ps_cmd);
3847 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003848 retval = restartjob(jp, mode);
3849 } while (*argv && *++argv);
3850 return retval;
3851}
3852#endif
3853
3854static int
3855sprint_status(char *s, int status, int sigonly)
3856{
3857 int col;
3858 int st;
3859
3860 col = 0;
3861 if (!WIFEXITED(status)) {
3862#if JOBS
3863 if (WIFSTOPPED(status))
3864 st = WSTOPSIG(status);
3865 else
3866#endif
3867 st = WTERMSIG(status);
3868 if (sigonly) {
3869 if (st == SIGINT || st == SIGPIPE)
3870 goto out;
3871#if JOBS
3872 if (WIFSTOPPED(status))
3873 goto out;
3874#endif
3875 }
3876 st &= 0x7f;
3877 col = fmtstr(s, 32, strsignal(st));
3878 if (WCOREDUMP(status)) {
3879 col += fmtstr(s + col, 16, " (core dumped)");
3880 }
3881 } else if (!sigonly) {
3882 st = WEXITSTATUS(status);
3883 if (st)
3884 col = fmtstr(s, 16, "Done(%d)", st);
3885 else
3886 col = fmtstr(s, 16, "Done");
3887 }
3888 out:
3889 return col;
3890}
3891
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003892static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003893dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003894{
3895 int pid;
3896 int status;
3897 struct job *jp;
3898 struct job *thisjob;
3899 int state;
3900
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003901 TRACE(("dowait(0x%x) called\n", wait_flags));
3902
3903 /* Do a wait system call. If job control is compiled in, we accept
3904 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3905 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003906 if (doing_jobctl)
3907 wait_flags |= WUNTRACED;
3908 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003909 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3910 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003911 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003912 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003913
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003914 INT_OFF;
3915 thisjob = NULL;
3916 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003917 struct procstat *ps;
3918 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003919 if (jp->state == JOBDONE)
3920 continue;
3921 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003922 ps = jp->ps;
3923 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003924 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003925 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003926 TRACE(("Job %d: changing status of proc %d "
3927 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003928 jobno(jp), pid, ps->ps_status, status));
3929 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003930 thisjob = jp;
3931 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003932 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003933 state = JOBRUNNING;
3934#if JOBS
3935 if (state == JOBRUNNING)
3936 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003937 if (WIFSTOPPED(ps->ps_status)) {
3938 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003939 state = JOBSTOPPED;
3940 }
3941#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003942 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003943 if (thisjob)
3944 goto gotjob;
3945 }
3946#if JOBS
3947 if (!WIFSTOPPED(status))
3948#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 jobless--;
3950 goto out;
3951
3952 gotjob:
3953 if (state != JOBRUNNING) {
3954 thisjob->changed = 1;
3955
3956 if (thisjob->state != state) {
3957 TRACE(("Job %d: changing state from %d to %d\n",
3958 jobno(thisjob), thisjob->state, state));
3959 thisjob->state = state;
3960#if JOBS
3961 if (state == JOBSTOPPED) {
3962 set_curjob(thisjob, CUR_STOPPED);
3963 }
3964#endif
3965 }
3966 }
3967
3968 out:
3969 INT_ON;
3970
3971 if (thisjob && thisjob == job) {
3972 char s[48 + 1];
3973 int len;
3974
3975 len = sprint_status(s, status, 1);
3976 if (len) {
3977 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003978 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003979 out2str(s);
3980 }
3981 }
3982 return pid;
3983}
3984
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003985static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003986blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003987{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003988 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003989 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003990 raise_exception(EXSIG);
3991 return pid;
3992}
3993
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003994#if JOBS
3995static void
3996showjob(FILE *out, struct job *jp, int mode)
3997{
3998 struct procstat *ps;
3999 struct procstat *psend;
4000 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004001 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004002 char s[80];
4003
4004 ps = jp->ps;
4005
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004006 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004007 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004008 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004009 return;
4010 }
4011
4012 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004013 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004014
4015 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004016 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004017 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004018 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004019
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004020 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004021 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004022
4023 psend = ps + jp->nprocs;
4024
4025 if (jp->state == JOBRUNNING) {
4026 strcpy(s + col, "Running");
4027 col += sizeof("Running") - 1;
4028 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004029 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004030 if (jp->state == JOBSTOPPED)
4031 status = jp->stopstatus;
4032 col += sprint_status(s + col, status, 0);
4033 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004034 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004035
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004036 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4037 * or prints several "PID | <cmdN>" lines,
4038 * depending on SHOW_PIDS bit.
4039 * We do not print status of individual processes
4040 * between PID and <cmdN>. bash does it, but not very well:
4041 * first line shows overall job status, not process status,
4042 * making it impossible to know 1st process status.
4043 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004044 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004045 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004046 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004047 s[0] = '\0';
4048 col = 33;
4049 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004050 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004052 fprintf(out, "%s%*c%s%s",
4053 s,
4054 33 - col >= 0 ? 33 - col : 0, ' ',
4055 ps == jp->ps ? "" : "| ",
4056 ps->ps_cmd
4057 );
4058 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004059 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004060
4061 jp->changed = 0;
4062
4063 if (jp->state == JOBDONE) {
4064 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4065 freejob(jp);
4066 }
4067}
4068
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004069/*
4070 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4071 * statuses have changed since the last call to showjobs.
4072 */
4073static void
4074showjobs(FILE *out, int mode)
4075{
4076 struct job *jp;
4077
Denys Vlasenko883cea42009-07-11 15:31:59 +02004078 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004079
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004080 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004081 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082 continue;
4083
4084 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004085 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004086 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004087 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 }
4089}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004090
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004091static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004092jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004093{
4094 int mode, m;
4095
4096 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004097 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004098 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004099 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004100 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004101 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004102 }
4103
4104 argv = argptr;
4105 if (*argv) {
4106 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004107 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004108 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004109 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004110 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004111 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004112
4113 return 0;
4114}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004115#endif /* JOBS */
4116
Michael Abbott359da5e2009-12-04 23:03:29 +01004117/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004118static int
4119getstatus(struct job *job)
4120{
4121 int status;
4122 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004123 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004124
Michael Abbott359da5e2009-12-04 23:03:29 +01004125 /* Fetch last member's status */
4126 ps = job->ps + job->nprocs - 1;
4127 status = ps->ps_status;
4128 if (pipefail) {
4129 /* "set -o pipefail" mode: use last _nonzero_ status */
4130 while (status == 0 && --ps >= job->ps)
4131 status = ps->ps_status;
4132 }
4133
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134 retval = WEXITSTATUS(status);
4135 if (!WIFEXITED(status)) {
4136#if JOBS
4137 retval = WSTOPSIG(status);
4138 if (!WIFSTOPPED(status))
4139#endif
4140 {
4141 /* XXX: limits number of signals */
4142 retval = WTERMSIG(status);
4143#if JOBS
4144 if (retval == SIGINT)
4145 job->sigint = 1;
4146#endif
4147 }
4148 retval += 128;
4149 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004150 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004151 jobno(job), job->nprocs, status, retval));
4152 return retval;
4153}
4154
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004155static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004156waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004157{
4158 struct job *job;
4159 int retval;
4160 struct job *jp;
4161
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004162 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004163 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004164
4165 nextopt(nullstr);
4166 retval = 0;
4167
4168 argv = argptr;
4169 if (!*argv) {
4170 /* wait for all jobs */
4171 for (;;) {
4172 jp = curjob;
4173 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004174 if (!jp) /* no running procs */
4175 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176 if (jp->state == JOBRUNNING)
4177 break;
4178 jp->waited = 1;
4179 jp = jp->prev_job;
4180 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004181 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004182 /* man bash:
4183 * "When bash is waiting for an asynchronous command via
4184 * the wait builtin, the reception of a signal for which a trap
4185 * has been set will cause the wait builtin to return immediately
4186 * with an exit status greater than 128, immediately after which
4187 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004188 *
4189 * blocking_wait_with_raise_on_sig raises signal handlers
4190 * if it gets no pid (pid < 0). However,
4191 * if child sends us a signal *and immediately exits*,
4192 * blocking_wait_with_raise_on_sig gets pid > 0
4193 * and does not handle pending_sig. Check this case: */
4194 if (pending_sig)
4195 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004196 }
4197 }
4198
4199 retval = 127;
4200 do {
4201 if (**argv != '%') {
4202 pid_t pid = number(*argv);
4203 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004204 while (1) {
4205 if (!job)
4206 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004207 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004208 break;
4209 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004210 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004211 } else
4212 job = getjob(*argv, 0);
4213 /* loop until process terminated or stopped */
4214 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004215 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004216 job->waited = 1;
4217 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004218 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004219 } while (*++argv);
4220
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004221 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004222 return retval;
4223}
4224
4225static struct job *
4226growjobtab(void)
4227{
4228 size_t len;
4229 ptrdiff_t offset;
4230 struct job *jp, *jq;
4231
4232 len = njobs * sizeof(*jp);
4233 jq = jobtab;
4234 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4235
4236 offset = (char *)jp - (char *)jq;
4237 if (offset) {
4238 /* Relocate pointers */
4239 size_t l = len;
4240
4241 jq = (struct job *)((char *)jq + l);
4242 while (l) {
4243 l -= sizeof(*jp);
4244 jq--;
4245#define joff(p) ((struct job *)((char *)(p) + l))
4246#define jmove(p) (p) = (void *)((char *)(p) + offset)
4247 if (joff(jp)->ps == &jq->ps0)
4248 jmove(joff(jp)->ps);
4249 if (joff(jp)->prev_job)
4250 jmove(joff(jp)->prev_job);
4251 }
4252 if (curjob)
4253 jmove(curjob);
4254#undef joff
4255#undef jmove
4256 }
4257
4258 njobs += 4;
4259 jobtab = jp;
4260 jp = (struct job *)((char *)jp + len);
4261 jq = jp + 3;
4262 do {
4263 jq->used = 0;
4264 } while (--jq >= jp);
4265 return jp;
4266}
4267
4268/*
4269 * Return a new job structure.
4270 * Called with interrupts off.
4271 */
4272static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004273makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004274{
4275 int i;
4276 struct job *jp;
4277
4278 for (i = njobs, jp = jobtab; ; jp++) {
4279 if (--i < 0) {
4280 jp = growjobtab();
4281 break;
4282 }
4283 if (jp->used == 0)
4284 break;
4285 if (jp->state != JOBDONE || !jp->waited)
4286 continue;
4287#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004288 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004289 continue;
4290#endif
4291 freejob(jp);
4292 break;
4293 }
4294 memset(jp, 0, sizeof(*jp));
4295#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004296 /* jp->jobctl is a bitfield.
4297 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004298 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004299 jp->jobctl = 1;
4300#endif
4301 jp->prev_job = curjob;
4302 curjob = jp;
4303 jp->used = 1;
4304 jp->ps = &jp->ps0;
4305 if (nprocs > 1) {
4306 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4307 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004308 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004309 jobno(jp)));
4310 return jp;
4311}
4312
4313#if JOBS
4314/*
4315 * Return a string identifying a command (to be printed by the
4316 * jobs command).
4317 */
4318static char *cmdnextc;
4319
4320static void
4321cmdputs(const char *s)
4322{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004323 static const char vstype[VSTYPE + 1][3] = {
4324 "", "}", "-", "+", "?", "=",
4325 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004326 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004327 };
4328
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004329 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004330 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004331 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004332 unsigned char c;
4333 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004334 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004335
Denys Vlasenko46a14772009-12-10 21:27:13 +01004336 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004337 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4338 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004339 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004340 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004341 switch (c) {
4342 case CTLESC:
4343 c = *p++;
4344 break;
4345 case CTLVAR:
4346 subtype = *p++;
4347 if ((subtype & VSTYPE) == VSLENGTH)
4348 str = "${#";
4349 else
4350 str = "${";
4351 if (!(subtype & VSQUOTE) == !(quoted & 1))
4352 goto dostr;
4353 quoted ^= 1;
4354 c = '"';
4355 break;
4356 case CTLENDVAR:
4357 str = "\"}" + !(quoted & 1);
4358 quoted >>= 1;
4359 subtype = 0;
4360 goto dostr;
4361 case CTLBACKQ:
4362 str = "$(...)";
4363 goto dostr;
4364 case CTLBACKQ+CTLQUOTE:
4365 str = "\"$(...)\"";
4366 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004367#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004368 case CTLARI:
4369 str = "$((";
4370 goto dostr;
4371 case CTLENDARI:
4372 str = "))";
4373 goto dostr;
4374#endif
4375 case CTLQUOTEMARK:
4376 quoted ^= 1;
4377 c = '"';
4378 break;
4379 case '=':
4380 if (subtype == 0)
4381 break;
4382 if ((subtype & VSTYPE) != VSNORMAL)
4383 quoted <<= 1;
4384 str = vstype[subtype & VSTYPE];
4385 if (subtype & VSNUL)
4386 c = ':';
4387 else
4388 goto checkstr;
4389 break;
4390 case '\'':
4391 case '\\':
4392 case '"':
4393 case '$':
4394 /* These can only happen inside quotes */
4395 cc[0] = c;
4396 str = cc;
4397 c = '\\';
4398 break;
4399 default:
4400 break;
4401 }
4402 USTPUTC(c, nextc);
4403 checkstr:
4404 if (!str)
4405 continue;
4406 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004407 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004408 USTPUTC(c, nextc);
4409 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004410 } /* while *p++ not NUL */
4411
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004412 if (quoted & 1) {
4413 USTPUTC('"', nextc);
4414 }
4415 *nextc = 0;
4416 cmdnextc = nextc;
4417}
4418
4419/* cmdtxt() and cmdlist() call each other */
4420static void cmdtxt(union node *n);
4421
4422static void
4423cmdlist(union node *np, int sep)
4424{
4425 for (; np; np = np->narg.next) {
4426 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004427 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004428 cmdtxt(np);
4429 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004430 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004431 }
4432}
4433
4434static void
4435cmdtxt(union node *n)
4436{
4437 union node *np;
4438 struct nodelist *lp;
4439 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004440
4441 if (!n)
4442 return;
4443 switch (n->type) {
4444 default:
4445#if DEBUG
4446 abort();
4447#endif
4448 case NPIPE:
4449 lp = n->npipe.cmdlist;
4450 for (;;) {
4451 cmdtxt(lp->n);
4452 lp = lp->next;
4453 if (!lp)
4454 break;
4455 cmdputs(" | ");
4456 }
4457 break;
4458 case NSEMI:
4459 p = "; ";
4460 goto binop;
4461 case NAND:
4462 p = " && ";
4463 goto binop;
4464 case NOR:
4465 p = " || ";
4466 binop:
4467 cmdtxt(n->nbinary.ch1);
4468 cmdputs(p);
4469 n = n->nbinary.ch2;
4470 goto donode;
4471 case NREDIR:
4472 case NBACKGND:
4473 n = n->nredir.n;
4474 goto donode;
4475 case NNOT:
4476 cmdputs("!");
4477 n = n->nnot.com;
4478 donode:
4479 cmdtxt(n);
4480 break;
4481 case NIF:
4482 cmdputs("if ");
4483 cmdtxt(n->nif.test);
4484 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004486 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004487 cmdputs("; else ");
4488 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004489 } else {
4490 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004491 }
4492 p = "; fi";
4493 goto dotail;
4494 case NSUBSHELL:
4495 cmdputs("(");
4496 n = n->nredir.n;
4497 p = ")";
4498 goto dotail;
4499 case NWHILE:
4500 p = "while ";
4501 goto until;
4502 case NUNTIL:
4503 p = "until ";
4504 until:
4505 cmdputs(p);
4506 cmdtxt(n->nbinary.ch1);
4507 n = n->nbinary.ch2;
4508 p = "; done";
4509 dodo:
4510 cmdputs("; do ");
4511 dotail:
4512 cmdtxt(n);
4513 goto dotail2;
4514 case NFOR:
4515 cmdputs("for ");
4516 cmdputs(n->nfor.var);
4517 cmdputs(" in ");
4518 cmdlist(n->nfor.args, 1);
4519 n = n->nfor.body;
4520 p = "; done";
4521 goto dodo;
4522 case NDEFUN:
4523 cmdputs(n->narg.text);
4524 p = "() { ... }";
4525 goto dotail2;
4526 case NCMD:
4527 cmdlist(n->ncmd.args, 1);
4528 cmdlist(n->ncmd.redirect, 0);
4529 break;
4530 case NARG:
4531 p = n->narg.text;
4532 dotail2:
4533 cmdputs(p);
4534 break;
4535 case NHERE:
4536 case NXHERE:
4537 p = "<<...";
4538 goto dotail2;
4539 case NCASE:
4540 cmdputs("case ");
4541 cmdputs(n->ncase.expr->narg.text);
4542 cmdputs(" in ");
4543 for (np = n->ncase.cases; np; np = np->nclist.next) {
4544 cmdtxt(np->nclist.pattern);
4545 cmdputs(") ");
4546 cmdtxt(np->nclist.body);
4547 cmdputs(";; ");
4548 }
4549 p = "esac";
4550 goto dotail2;
4551 case NTO:
4552 p = ">";
4553 goto redir;
4554 case NCLOBBER:
4555 p = ">|";
4556 goto redir;
4557 case NAPPEND:
4558 p = ">>";
4559 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004560#if ENABLE_ASH_BASH_COMPAT
4561 case NTO2:
4562#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004563 case NTOFD:
4564 p = ">&";
4565 goto redir;
4566 case NFROM:
4567 p = "<";
4568 goto redir;
4569 case NFROMFD:
4570 p = "<&";
4571 goto redir;
4572 case NFROMTO:
4573 p = "<>";
4574 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004575 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004576 cmdputs(p);
4577 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004578 cmdputs(utoa(n->ndup.dupfd));
4579 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004580 }
4581 n = n->nfile.fname;
4582 goto donode;
4583 }
4584}
4585
4586static char *
4587commandtext(union node *n)
4588{
4589 char *name;
4590
4591 STARTSTACKSTR(cmdnextc);
4592 cmdtxt(n);
4593 name = stackblock();
4594 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4595 name, cmdnextc, cmdnextc));
4596 return ckstrdup(name);
4597}
4598#endif /* JOBS */
4599
4600/*
4601 * Fork off a subshell. If we are doing job control, give the subshell its
4602 * own process group. Jp is a job structure that the job is to be added to.
4603 * N is the command that will be evaluated by the child. Both jp and n may
4604 * be NULL. The mode parameter can be one of the following:
4605 * FORK_FG - Fork off a foreground process.
4606 * FORK_BG - Fork off a background process.
4607 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4608 * process group even if job control is on.
4609 *
4610 * When job control is turned off, background processes have their standard
4611 * input redirected to /dev/null (except for the second and later processes
4612 * in a pipeline).
4613 *
4614 * Called with interrupts off.
4615 */
4616/*
4617 * Clear traps on a fork.
4618 */
4619static void
4620clear_traps(void)
4621{
4622 char **tp;
4623
4624 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004625 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004626 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004627 if (trap_ptr == trap)
4628 free(*tp);
4629 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004630 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004631 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004632 setsignal(tp - trap);
4633 INT_ON;
4634 }
4635 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004636 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004637}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004638
4639/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004640static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004641
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004642/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004643static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004644forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004645{
4646 int oldlvl;
4647
4648 TRACE(("Child shell %d\n", getpid()));
4649 oldlvl = shlvl;
4650 shlvl++;
4651
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004652 /* man bash: "Non-builtin commands run by bash have signal handlers
4653 * set to the values inherited by the shell from its parent".
4654 * Do we do it correctly? */
4655
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004656 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004657
4658 if (mode == FORK_NOJOB /* is it `xxx` ? */
4659 && n && n->type == NCMD /* is it single cmd? */
4660 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004661 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004662 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4663 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4664 ) {
4665 TRACE(("Trap hack\n"));
4666 /* Awful hack for `trap` or $(trap).
4667 *
4668 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4669 * contains an example where "trap" is executed in a subshell:
4670 *
4671 * save_traps=$(trap)
4672 * ...
4673 * eval "$save_traps"
4674 *
4675 * Standard does not say that "trap" in subshell shall print
4676 * parent shell's traps. It only says that its output
4677 * must have suitable form, but then, in the above example
4678 * (which is not supposed to be normative), it implies that.
4679 *
4680 * bash (and probably other shell) does implement it
4681 * (traps are reset to defaults, but "trap" still shows them),
4682 * but as a result, "trap" logic is hopelessly messed up:
4683 *
4684 * # trap
4685 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4686 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4687 * # true | trap <--- trap is in subshell - no output (ditto)
4688 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4689 * trap -- 'echo Ho' SIGWINCH
4690 * # echo `(trap)` <--- in subshell in subshell - output
4691 * trap -- 'echo Ho' SIGWINCH
4692 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4693 * trap -- 'echo Ho' SIGWINCH
4694 *
4695 * The rules when to forget and when to not forget traps
4696 * get really complex and nonsensical.
4697 *
4698 * Our solution: ONLY bare $(trap) or `trap` is special.
4699 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004700 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004701 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004702 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004703 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004704 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004705#if JOBS
4706 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004707 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004708 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4709 pid_t pgrp;
4710
4711 if (jp->nprocs == 0)
4712 pgrp = getpid();
4713 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004714 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004715 /* this can fail because we are doing it in the parent also */
4716 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004717 if (mode == FORK_FG)
4718 xtcsetpgrp(ttyfd, pgrp);
4719 setsignal(SIGTSTP);
4720 setsignal(SIGTTOU);
4721 } else
4722#endif
4723 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004724 /* man bash: "When job control is not in effect,
4725 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004726 ignoresig(SIGINT);
4727 ignoresig(SIGQUIT);
4728 if (jp->nprocs == 0) {
4729 close(0);
4730 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004731 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004732 }
4733 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004734 if (!oldlvl) {
4735 if (iflag) { /* why if iflag only? */
4736 setsignal(SIGINT);
4737 setsignal(SIGTERM);
4738 }
4739 /* man bash:
4740 * "In all cases, bash ignores SIGQUIT. Non-builtin
4741 * commands run by bash have signal handlers
4742 * set to the values inherited by the shell
4743 * from its parent".
4744 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004745 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004746 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004747#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004748 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004749 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004750 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004751 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004752 /* "jobs": we do not want to clear job list for it,
4753 * instead we remove only _its_ own_ job from job list.
4754 * This makes "jobs .... | cat" more useful.
4755 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004756 freejob(curjob);
4757 return;
4758 }
4759#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004760 for (jp = curjob; jp; jp = jp->prev_job)
4761 freejob(jp);
4762 jobless = 0;
4763}
4764
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004765/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004766#if !JOBS
4767#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4768#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004769static void
4770forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4771{
4772 TRACE(("In parent shell: child = %d\n", pid));
4773 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004774 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4775 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004776 jobless++;
4777 return;
4778 }
4779#if JOBS
4780 if (mode != FORK_NOJOB && jp->jobctl) {
4781 int pgrp;
4782
4783 if (jp->nprocs == 0)
4784 pgrp = pid;
4785 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004786 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004787 /* This can fail because we are doing it in the child also */
4788 setpgid(pid, pgrp);
4789 }
4790#endif
4791 if (mode == FORK_BG) {
4792 backgndpid = pid; /* set $! */
4793 set_curjob(jp, CUR_RUNNING);
4794 }
4795 if (jp) {
4796 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004797 ps->ps_pid = pid;
4798 ps->ps_status = -1;
4799 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004800#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004801 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004802 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004803#endif
4804 }
4805}
4806
4807static int
4808forkshell(struct job *jp, union node *n, int mode)
4809{
4810 int pid;
4811
4812 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4813 pid = fork();
4814 if (pid < 0) {
4815 TRACE(("Fork failed, errno=%d", errno));
4816 if (jp)
4817 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004818 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004819 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004820 if (pid == 0) {
4821 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004822 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004823 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004824 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004825 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004826 return pid;
4827}
4828
4829/*
4830 * Wait for job to finish.
4831 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004832 * Under job control we have the problem that while a child process
4833 * is running interrupts generated by the user are sent to the child
4834 * but not to the shell. This means that an infinite loop started by
4835 * an interactive user may be hard to kill. With job control turned off,
4836 * an interactive user may place an interactive program inside a loop.
4837 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004838 * these interrupts to also abort the loop. The approach we take here
4839 * is to have the shell ignore interrupt signals while waiting for a
4840 * foreground process to terminate, and then send itself an interrupt
4841 * signal if the child process was terminated by an interrupt signal.
4842 * Unfortunately, some programs want to do a bit of cleanup and then
4843 * exit on interrupt; unless these processes terminate themselves by
4844 * sending a signal to themselves (instead of calling exit) they will
4845 * confuse this approach.
4846 *
4847 * Called with interrupts off.
4848 */
4849static int
4850waitforjob(struct job *jp)
4851{
4852 int st;
4853
4854 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004855
4856 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004857 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004858 /* In non-interactive shells, we _can_ get
4859 * a keyboard signal here and be EINTRed,
4860 * but we just loop back, waiting for command to complete.
4861 *
4862 * man bash:
4863 * "If bash is waiting for a command to complete and receives
4864 * a signal for which a trap has been set, the trap
4865 * will not be executed until the command completes."
4866 *
4867 * Reality is that even if trap is not set, bash
4868 * will not act on the signal until command completes.
4869 * Try this. sleep5intoff.c:
4870 * #include <signal.h>
4871 * #include <unistd.h>
4872 * int main() {
4873 * sigset_t set;
4874 * sigemptyset(&set);
4875 * sigaddset(&set, SIGINT);
4876 * sigaddset(&set, SIGQUIT);
4877 * sigprocmask(SIG_BLOCK, &set, NULL);
4878 * sleep(5);
4879 * return 0;
4880 * }
4881 * $ bash -c './sleep5intoff; echo hi'
4882 * ^C^C^C^C <--- pressing ^C once a second
4883 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004884 * $ bash -c './sleep5intoff; echo hi'
4885 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4886 * $ _
4887 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004888 dowait(DOWAIT_BLOCK, jp);
4889 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004890 INT_ON;
4891
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004892 st = getstatus(jp);
4893#if JOBS
4894 if (jp->jobctl) {
4895 xtcsetpgrp(ttyfd, rootpid);
4896 /*
4897 * This is truly gross.
4898 * If we're doing job control, then we did a TIOCSPGRP which
4899 * caused us (the shell) to no longer be in the controlling
4900 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4901 * intuit from the subprocess exit status whether a SIGINT
4902 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4903 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004904 if (jp->sigint) /* TODO: do the same with all signals */
4905 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004906 }
4907 if (jp->state == JOBDONE)
4908#endif
4909 freejob(jp);
4910 return st;
4911}
4912
4913/*
4914 * return 1 if there are stopped jobs, otherwise 0
4915 */
4916static int
4917stoppedjobs(void)
4918{
4919 struct job *jp;
4920 int retval;
4921
4922 retval = 0;
4923 if (job_warning)
4924 goto out;
4925 jp = curjob;
4926 if (jp && jp->state == JOBSTOPPED) {
4927 out2str("You have stopped jobs.\n");
4928 job_warning = 2;
4929 retval++;
4930 }
4931 out:
4932 return retval;
4933}
4934
4935
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004936/* ============ redir.c
4937 *
4938 * Code for dealing with input/output redirection.
4939 */
4940
4941#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004942#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004943
4944/*
4945 * Open a file in noclobber mode.
4946 * The code was copied from bash.
4947 */
4948static int
4949noclobberopen(const char *fname)
4950{
4951 int r, fd;
4952 struct stat finfo, finfo2;
4953
4954 /*
4955 * If the file exists and is a regular file, return an error
4956 * immediately.
4957 */
4958 r = stat(fname, &finfo);
4959 if (r == 0 && S_ISREG(finfo.st_mode)) {
4960 errno = EEXIST;
4961 return -1;
4962 }
4963
4964 /*
4965 * If the file was not present (r != 0), make sure we open it
4966 * exclusively so that if it is created before we open it, our open
4967 * will fail. Make sure that we do not truncate an existing file.
4968 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4969 * file was not a regular file, we leave O_EXCL off.
4970 */
4971 if (r != 0)
4972 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4973 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4974
4975 /* If the open failed, return the file descriptor right away. */
4976 if (fd < 0)
4977 return fd;
4978
4979 /*
4980 * OK, the open succeeded, but the file may have been changed from a
4981 * non-regular file to a regular file between the stat and the open.
4982 * We are assuming that the O_EXCL open handles the case where FILENAME
4983 * did not exist and is symlinked to an existing file between the stat
4984 * and open.
4985 */
4986
4987 /*
4988 * If we can open it and fstat the file descriptor, and neither check
4989 * revealed that it was a regular file, and the file has not been
4990 * replaced, return the file descriptor.
4991 */
4992 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4993 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4994 return fd;
4995
4996 /* The file has been replaced. badness. */
4997 close(fd);
4998 errno = EEXIST;
4999 return -1;
5000}
5001
5002/*
5003 * Handle here documents. Normally we fork off a process to write the
5004 * data to a pipe. If the document is short, we can stuff the data in
5005 * the pipe without forking.
5006 */
5007/* openhere needs this forward reference */
5008static void expandhere(union node *arg, int fd);
5009static int
5010openhere(union node *redir)
5011{
5012 int pip[2];
5013 size_t len = 0;
5014
5015 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005016 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005017 if (redir->type == NHERE) {
5018 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005019 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005020 full_write(pip[1], redir->nhere.doc->narg.text, len);
5021 goto out;
5022 }
5023 }
5024 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005025 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005026 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005027 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5028 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5029 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5030 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005031 signal(SIGPIPE, SIG_DFL);
5032 if (redir->type == NHERE)
5033 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005034 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005035 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005036 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005037 }
5038 out:
5039 close(pip[1]);
5040 return pip[0];
5041}
5042
5043static int
5044openredirect(union node *redir)
5045{
5046 char *fname;
5047 int f;
5048
5049 switch (redir->nfile.type) {
5050 case NFROM:
5051 fname = redir->nfile.expfname;
5052 f = open(fname, O_RDONLY);
5053 if (f < 0)
5054 goto eopen;
5055 break;
5056 case NFROMTO:
5057 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005058 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005059 if (f < 0)
5060 goto ecreate;
5061 break;
5062 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005063#if ENABLE_ASH_BASH_COMPAT
5064 case NTO2:
5065#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005066 /* Take care of noclobber mode. */
5067 if (Cflag) {
5068 fname = redir->nfile.expfname;
5069 f = noclobberopen(fname);
5070 if (f < 0)
5071 goto ecreate;
5072 break;
5073 }
5074 /* FALLTHROUGH */
5075 case NCLOBBER:
5076 fname = redir->nfile.expfname;
5077 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5078 if (f < 0)
5079 goto ecreate;
5080 break;
5081 case NAPPEND:
5082 fname = redir->nfile.expfname;
5083 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5084 if (f < 0)
5085 goto ecreate;
5086 break;
5087 default:
5088#if DEBUG
5089 abort();
5090#endif
5091 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005092/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005093// case NTOFD:
5094// case NFROMFD:
5095// f = -1;
5096// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005097 case NHERE:
5098 case NXHERE:
5099 f = openhere(redir);
5100 break;
5101 }
5102
5103 return f;
5104 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005105 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005106 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005107 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005108}
5109
5110/*
5111 * Copy a file descriptor to be >= to. Returns -1
5112 * if the source file descriptor is closed, EMPTY if there are no unused
5113 * file descriptors left.
5114 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005115/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5116 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005117enum {
5118 COPYFD_EXACT = (int)~(INT_MAX),
5119 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5120};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005121static int
5122copyfd(int from, int to)
5123{
5124 int newfd;
5125
Denis Vlasenko5a867312008-07-24 19:46:38 +00005126 if (to & COPYFD_EXACT) {
5127 to &= ~COPYFD_EXACT;
5128 /*if (from != to)*/
5129 newfd = dup2(from, to);
5130 } else {
5131 newfd = fcntl(from, F_DUPFD, to);
5132 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005133 if (newfd < 0) {
5134 if (errno == EMFILE)
5135 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005136 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005137 ash_msg_and_raise_error("%d: %m", from);
5138 }
5139 return newfd;
5140}
5141
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005142/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005143struct two_fd_t {
5144 int orig, copy;
5145};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005146struct redirtab {
5147 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005148 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005149 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005150 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005151};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005152#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005153
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005154static int need_to_remember(struct redirtab *rp, int fd)
5155{
5156 int i;
5157
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005158 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005159 return 0;
5160
5161 for (i = 0; i < rp->pair_count; i++) {
5162 if (rp->two_fd[i].orig == fd) {
5163 /* already remembered */
5164 return 0;
5165 }
5166 }
5167 return 1;
5168}
5169
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005170/* "hidden" fd is a fd used to read scripts, or a copy of such */
5171static int is_hidden_fd(struct redirtab *rp, int fd)
5172{
5173 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005174 struct parsefile *pf;
5175
5176 if (fd == -1)
5177 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005178 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005179 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005180 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005181 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005182 * $ ash # running ash interactively
5183 * $ . ./script.sh
5184 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005185 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005186 * it's still ok to use it: "read" builtin uses it,
5187 * why should we cripple "exec" builtin?
5188 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005189 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005190 return 1;
5191 }
5192 pf = pf->prev;
5193 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005194
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005195 if (!rp)
5196 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005197 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005198 fd |= COPYFD_RESTORE;
5199 for (i = 0; i < rp->pair_count; i++) {
5200 if (rp->two_fd[i].copy == fd) {
5201 return 1;
5202 }
5203 }
5204 return 0;
5205}
5206
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005207/*
5208 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5209 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005210 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005211 */
5212/* flags passed to redirect */
5213#define REDIR_PUSH 01 /* save previous values of file descriptors */
5214#define REDIR_SAVEFD2 03 /* set preverrout */
5215static void
5216redirect(union node *redir, int flags)
5217{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005218 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005219 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005220 int i;
5221 int fd;
5222 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005223 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005224
Denis Vlasenko01631112007-12-16 17:20:38 +00005225 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005226 if (!redir) {
5227 return;
5228 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005229
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005230 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005231 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005232 INT_OFF;
5233 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005234 union node *tmp = redir;
5235 do {
5236 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005237#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005238 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005239 sv_pos++;
5240#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005241 tmp = tmp->nfile.next;
5242 } while (tmp);
5243 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005244 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005245 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005246 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005247 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005248 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005249 while (sv_pos > 0) {
5250 sv_pos--;
5251 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5252 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005253 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005254
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005255 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005256 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005257 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005258 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005259 right_fd = redir->ndup.dupfd;
5260 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005261 /* redirect from/to same file descriptor? */
5262 if (right_fd == fd)
5263 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005264 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005265 if (is_hidden_fd(sv, right_fd)) {
5266 errno = EBADF; /* as if it is closed */
5267 ash_msg_and_raise_error("%d: %m", right_fd);
5268 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005269 newfd = -1;
5270 } else {
5271 newfd = openredirect(redir); /* always >= 0 */
5272 if (fd == newfd) {
5273 /* Descriptor wasn't open before redirect.
5274 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005275 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005276 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005277 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005278 continue;
5279 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005280 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005281#if ENABLE_ASH_BASH_COMPAT
5282 redirect_more:
5283#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005284 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005285 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005286 /* Careful to not accidentally "save"
5287 * to the same fd as right side fd in N>&M */
5288 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5289 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005290/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5291 * are closed in popredir() in the child, preventing them from leaking
5292 * into child. (popredir() also cleans up the mess in case of failures)
5293 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005294 if (i == -1) {
5295 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005296 if (i != EBADF) {
5297 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005298 if (newfd >= 0)
5299 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005300 errno = i;
5301 ash_msg_and_raise_error("%d: %m", fd);
5302 /* NOTREACHED */
5303 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005304 /* EBADF: it is not open - good, remember to close it */
5305 remember_to_close:
5306 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005307 } else { /* fd is open, save its copy */
5308 /* "exec fd>&-" should not close fds
5309 * which point to script file(s).
5310 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005311 if (is_hidden_fd(sv, fd))
5312 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005313 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005314 if (fd == 2)
5315 copied_fd2 = i;
5316 sv->two_fd[sv_pos].orig = fd;
5317 sv->two_fd[sv_pos].copy = i;
5318 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005319 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005320 if (newfd < 0) {
5321 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005322 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005323 /* Don't want to trigger debugging */
5324 if (fd != -1)
5325 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005326 } else {
5327 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005328 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005329 } else if (fd != newfd) { /* move newfd to fd */
5330 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005331#if ENABLE_ASH_BASH_COMPAT
5332 if (!(redir->nfile.type == NTO2 && fd == 2))
5333#endif
5334 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005335 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005336#if ENABLE_ASH_BASH_COMPAT
5337 if (redir->nfile.type == NTO2 && fd == 1) {
5338 /* We already redirected it to fd 1, now copy it to 2 */
5339 newfd = 1;
5340 fd = 2;
5341 goto redirect_more;
5342 }
5343#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005344 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005345
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005346 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005347 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5348 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005349}
5350
5351/*
5352 * Undo the effects of the last redirection.
5353 */
5354static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005355popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005356{
5357 struct redirtab *rp;
5358 int i;
5359
Denis Vlasenko01631112007-12-16 17:20:38 +00005360 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005361 return;
5362 INT_OFF;
5363 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005364 for (i = 0; i < rp->pair_count; i++) {
5365 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005366 int copy = rp->two_fd[i].copy;
5367 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005368 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005369 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005370 continue;
5371 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005372 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005373 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005374 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005375 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005376 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005377 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005378 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005379 }
5380 }
5381 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005382 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005383 free(rp);
5384 INT_ON;
5385}
5386
5387/*
5388 * Undo all redirections. Called on error or interrupt.
5389 */
5390
5391/*
5392 * Discard all saved file descriptors.
5393 */
5394static void
5395clearredir(int drop)
5396{
5397 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005398 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005399 if (!redirlist)
5400 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005401 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005402 }
5403}
5404
5405static int
5406redirectsafe(union node *redir, int flags)
5407{
5408 int err;
5409 volatile int saveint;
5410 struct jmploc *volatile savehandler = exception_handler;
5411 struct jmploc jmploc;
5412
5413 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005414 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5415 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005416 if (!err) {
5417 exception_handler = &jmploc;
5418 redirect(redir, flags);
5419 }
5420 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005421 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005422 longjmp(exception_handler->loc, 1);
5423 RESTORE_INT(saveint);
5424 return err;
5425}
5426
5427
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005428/* ============ Routines to expand arguments to commands
5429 *
5430 * We have to deal with backquotes, shell variables, and file metacharacters.
5431 */
5432
Mike Frysinger98c52642009-04-02 10:02:37 +00005433#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005434static arith_t
5435ash_arith(const char *s)
5436{
5437 arith_eval_hooks_t math_hooks;
5438 arith_t result;
5439 int errcode = 0;
5440
5441 math_hooks.lookupvar = lookupvar;
Denys Vlasenko03dad222010-01-12 23:29:57 +01005442 math_hooks.setvar = setvar2;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005443 math_hooks.endofname = endofname;
5444
5445 INT_OFF;
5446 result = arith(s, &errcode, &math_hooks);
5447 if (errcode < 0) {
5448 if (errcode == -3)
5449 ash_msg_and_raise_error("exponent less than 0");
5450 if (errcode == -2)
5451 ash_msg_and_raise_error("divide by zero");
5452 if (errcode == -5)
5453 ash_msg_and_raise_error("expression recursion loop detected");
5454 raise_error_syntax(s);
5455 }
5456 INT_ON;
5457
5458 return result;
5459}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005460#endif
5461
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005462/*
5463 * expandarg flags
5464 */
5465#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5466#define EXP_TILDE 0x2 /* do normal tilde expansion */
5467#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5468#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5469#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5470#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5471#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5472#define EXP_WORD 0x80 /* expand word in parameter expansion */
5473#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5474/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005475 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005476 */
5477#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5478#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5479#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5480#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5481#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5482
5483/*
5484 * Structure specifying which parts of the string should be searched
5485 * for IFS characters.
5486 */
5487struct ifsregion {
5488 struct ifsregion *next; /* next region in list */
5489 int begoff; /* offset of start of region */
5490 int endoff; /* offset of end of region */
5491 int nulonly; /* search for nul bytes only */
5492};
5493
5494struct arglist {
5495 struct strlist *list;
5496 struct strlist **lastp;
5497};
5498
5499/* output of current string */
5500static char *expdest;
5501/* list of back quote expressions */
5502static struct nodelist *argbackq;
5503/* first struct in list of ifs regions */
5504static struct ifsregion ifsfirst;
5505/* last struct in list */
5506static struct ifsregion *ifslastp;
5507/* holds expanded arg list */
5508static struct arglist exparg;
5509
5510/*
5511 * Our own itoa().
5512 */
5513static int
5514cvtnum(arith_t num)
5515{
5516 int len;
5517
5518 expdest = makestrspace(32, expdest);
Mike Frysinger98c52642009-04-02 10:02:37 +00005519 len = fmtstr(expdest, 32, arith_t_fmt, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005520 STADJUST(len, expdest);
5521 return len;
5522}
5523
5524static size_t
5525esclen(const char *start, const char *p)
5526{
5527 size_t esc = 0;
5528
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005529 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005530 esc++;
5531 }
5532 return esc;
5533}
5534
5535/*
5536 * Remove any CTLESC characters from a string.
5537 */
5538static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005539rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005540{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005541 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005542
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005543 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005544 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005545 unsigned protect_against_glob;
5546 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005547
5548 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005549 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005550 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005551
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005552 q = p;
5553 r = str;
5554 if (flag & RMESCAPE_ALLOC) {
5555 size_t len = p - str;
5556 size_t fulllen = len + strlen(p) + 1;
5557
5558 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005559 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005560 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005561 /* p and str may be invalidated by makestrspace */
5562 str = (char *)stackblock() + strloc;
5563 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005564 } else if (flag & RMESCAPE_HEAP) {
5565 r = ckmalloc(fulllen);
5566 } else {
5567 r = stalloc(fulllen);
5568 }
5569 q = r;
5570 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005571 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005572 }
5573 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005574
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005575 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5576 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005577 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005578 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005579 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005580// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5581// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5582// Note: both inquotes and protect_against_glob only affect whether
5583// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005584 inquotes = ~inquotes;
5585 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005586 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005587 continue;
5588 }
5589 if (*p == '\\') {
5590 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005591 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005592 goto copy;
5593 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005594 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005595 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005596 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005597 *q++ = '\\';
5598 }
5599 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005600 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005601 copy:
5602 *q++ = *p++;
5603 }
5604 *q = '\0';
5605 if (flag & RMESCAPE_GROW) {
5606 expdest = r;
5607 STADJUST(q - r + 1, expdest);
5608 }
5609 return r;
5610}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005611#define pmatch(a, b) !fnmatch((a), (b), 0)
5612
5613/*
5614 * Prepare a pattern for a expmeta (internal glob(3)) call.
5615 *
5616 * Returns an stalloced string.
5617 */
5618static char *
5619preglob(const char *pattern, int quoted, int flag)
5620{
5621 flag |= RMESCAPE_GLOB;
5622 if (quoted) {
5623 flag |= RMESCAPE_QUOTED;
5624 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005625 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005626}
5627
5628/*
5629 * Put a string on the stack.
5630 */
5631static void
5632memtodest(const char *p, size_t len, int syntax, int quotes)
5633{
5634 char *q = expdest;
5635
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005636 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005637
5638 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005639 unsigned char c = *p++;
5640 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005641 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005642 if (quotes) {
5643 int n = SIT(c, syntax);
5644 if (n == CCTL || n == CBACK)
5645 USTPUTC(CTLESC, q);
5646 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005647 USTPUTC(c, q);
5648 }
5649
5650 expdest = q;
5651}
5652
5653static void
5654strtodest(const char *p, int syntax, int quotes)
5655{
5656 memtodest(p, strlen(p), syntax, quotes);
5657}
5658
5659/*
5660 * Record the fact that we have to scan this region of the
5661 * string for IFS characters.
5662 */
5663static void
5664recordregion(int start, int end, int nulonly)
5665{
5666 struct ifsregion *ifsp;
5667
5668 if (ifslastp == NULL) {
5669 ifsp = &ifsfirst;
5670 } else {
5671 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005672 ifsp = ckzalloc(sizeof(*ifsp));
5673 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005674 ifslastp->next = ifsp;
5675 INT_ON;
5676 }
5677 ifslastp = ifsp;
5678 ifslastp->begoff = start;
5679 ifslastp->endoff = end;
5680 ifslastp->nulonly = nulonly;
5681}
5682
5683static void
5684removerecordregions(int endoff)
5685{
5686 if (ifslastp == NULL)
5687 return;
5688
5689 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005690 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005691 struct ifsregion *ifsp;
5692 INT_OFF;
5693 ifsp = ifsfirst.next->next;
5694 free(ifsfirst.next);
5695 ifsfirst.next = ifsp;
5696 INT_ON;
5697 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005698 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005699 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005700 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005701 ifslastp = &ifsfirst;
5702 ifsfirst.endoff = endoff;
5703 }
5704 return;
5705 }
5706
5707 ifslastp = &ifsfirst;
5708 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005709 ifslastp = ifslastp->next;
5710 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005711 struct ifsregion *ifsp;
5712 INT_OFF;
5713 ifsp = ifslastp->next->next;
5714 free(ifslastp->next);
5715 ifslastp->next = ifsp;
5716 INT_ON;
5717 }
5718 if (ifslastp->endoff > endoff)
5719 ifslastp->endoff = endoff;
5720}
5721
5722static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005723exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005724{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005725 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005726 char *name;
5727 struct passwd *pw;
5728 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005729 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005730 int startloc;
5731
5732 name = p + 1;
5733
5734 while ((c = *++p) != '\0') {
5735 switch (c) {
5736 case CTLESC:
5737 return startp;
5738 case CTLQUOTEMARK:
5739 return startp;
5740 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005741 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005742 goto done;
5743 break;
5744 case '/':
5745 case CTLENDVAR:
5746 goto done;
5747 }
5748 }
5749 done:
5750 *p = '\0';
5751 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005752 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005753 } else {
5754 pw = getpwnam(name);
5755 if (pw == NULL)
5756 goto lose;
5757 home = pw->pw_dir;
5758 }
5759 if (!home || !*home)
5760 goto lose;
5761 *p = c;
5762 startloc = expdest - (char *)stackblock();
5763 strtodest(home, SQSYNTAX, quotes);
5764 recordregion(startloc, expdest - (char *)stackblock(), 0);
5765 return p;
5766 lose:
5767 *p = c;
5768 return startp;
5769}
5770
5771/*
5772 * Execute a command inside back quotes. If it's a builtin command, we
5773 * want to save its output in a block obtained from malloc. Otherwise
5774 * we fork off a subprocess and get the output of the command via a pipe.
5775 * Should be called with interrupts off.
5776 */
5777struct backcmd { /* result of evalbackcmd */
5778 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005779 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005780 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005781 struct job *jp; /* job structure for command */
5782};
5783
5784/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005785static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005786#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005787static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005788
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005789static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005790evalbackcmd(union node *n, struct backcmd *result)
5791{
5792 int saveherefd;
5793
5794 result->fd = -1;
5795 result->buf = NULL;
5796 result->nleft = 0;
5797 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005798 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005799 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005800
5801 saveherefd = herefd;
5802 herefd = -1;
5803
5804 {
5805 int pip[2];
5806 struct job *jp;
5807
5808 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005809 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005810 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005811 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5812 FORCE_INT_ON;
5813 close(pip[0]);
5814 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005815 /*close(1);*/
5816 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005817 close(pip[1]);
5818 }
5819 eflag = 0;
5820 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5821 /* NOTREACHED */
5822 }
5823 close(pip[1]);
5824 result->fd = pip[0];
5825 result->jp = jp;
5826 }
5827 herefd = saveherefd;
5828 out:
5829 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5830 result->fd, result->buf, result->nleft, result->jp));
5831}
5832
5833/*
5834 * Expand stuff in backwards quotes.
5835 */
5836static void
5837expbackq(union node *cmd, int quoted, int quotes)
5838{
5839 struct backcmd in;
5840 int i;
5841 char buf[128];
5842 char *p;
5843 char *dest;
5844 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005845 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005846 struct stackmark smark;
5847
5848 INT_OFF;
5849 setstackmark(&smark);
5850 dest = expdest;
5851 startloc = dest - (char *)stackblock();
5852 grabstackstr(dest);
5853 evalbackcmd(cmd, &in);
5854 popstackmark(&smark);
5855
5856 p = in.buf;
5857 i = in.nleft;
5858 if (i == 0)
5859 goto read;
5860 for (;;) {
5861 memtodest(p, i, syntax, quotes);
5862 read:
5863 if (in.fd < 0)
5864 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005865 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 TRACE(("expbackq: read returns %d\n", i));
5867 if (i <= 0)
5868 break;
5869 p = buf;
5870 }
5871
Denis Vlasenko60818682007-09-28 22:07:23 +00005872 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005873 if (in.fd >= 0) {
5874 close(in.fd);
5875 back_exitstatus = waitforjob(in.jp);
5876 }
5877 INT_ON;
5878
5879 /* Eat all trailing newlines */
5880 dest = expdest;
5881 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5882 STUNPUTC(dest);
5883 expdest = dest;
5884
5885 if (quoted == 0)
5886 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005887 TRACE(("evalbackq: size:%d:'%.*s'\n",
5888 (int)((dest - (char *)stackblock()) - startloc),
5889 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005890 stackblock() + startloc));
5891}
5892
Mike Frysinger98c52642009-04-02 10:02:37 +00005893#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005894/*
5895 * Expand arithmetic expression. Backup to start of expression,
5896 * evaluate, place result in (backed up) result, adjust string position.
5897 */
5898static void
5899expari(int quotes)
5900{
5901 char *p, *start;
5902 int begoff;
5903 int flag;
5904 int len;
5905
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005906 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005907
5908 /*
5909 * This routine is slightly over-complicated for
5910 * efficiency. Next we scan backwards looking for the
5911 * start of arithmetic.
5912 */
5913 start = stackblock();
5914 p = expdest - 1;
5915 *p = '\0';
5916 p--;
5917 do {
5918 int esc;
5919
Denys Vlasenkocd716832009-11-28 22:14:02 +01005920 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005921 p--;
5922#if DEBUG
5923 if (p < start) {
5924 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5925 }
5926#endif
5927 }
5928
5929 esc = esclen(start, p);
5930 if (!(esc % 2)) {
5931 break;
5932 }
5933
5934 p -= esc + 1;
5935 } while (1);
5936
5937 begoff = p - start;
5938
5939 removerecordregions(begoff);
5940
5941 flag = p[1];
5942
5943 expdest = p;
5944
5945 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005946 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005947
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005948 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005949
5950 if (flag != '"')
5951 recordregion(begoff, begoff + len, 0);
5952}
5953#endif
5954
5955/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005956static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005957
5958/*
5959 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5960 * characters to allow for further processing. Otherwise treat
5961 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005962 *
5963 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5964 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5965 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005966 */
5967static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005968argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005969{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005970 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005971 '=',
5972 ':',
5973 CTLQUOTEMARK,
5974 CTLENDVAR,
5975 CTLESC,
5976 CTLVAR,
5977 CTLBACKQ,
5978 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00005979#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005980 CTLENDARI,
5981#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01005982 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005983 };
5984 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005985 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
5986 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005987 int inquotes;
5988 size_t length;
5989 int startloc;
5990
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005991 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005992 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005993 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005994 reject++;
5995 }
5996 inquotes = 0;
5997 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005998 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005999 char *q;
6000
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006001 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006002 tilde:
6003 q = p;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006004 if (*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006005 q++;
6006 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006007 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006008 }
6009 start:
6010 startloc = expdest - (char *)stackblock();
6011 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006012 unsigned char c;
6013
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006014 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006015 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006016 if (c) {
6017 if (!(c & 0x80)
Mike Frysinger98c52642009-04-02 10:02:37 +00006018#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006019 || c == CTLENDARI
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006020#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006021 ) {
6022 /* c == '=' || c == ':' || c == CTLENDARI */
6023 length++;
6024 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025 }
6026 if (length > 0) {
6027 int newloc;
6028 expdest = stack_nputstr(p, length, expdest);
6029 newloc = expdest - (char *)stackblock();
6030 if (breakall && !inquotes && newloc > startloc) {
6031 recordregion(startloc, newloc, 0);
6032 }
6033 startloc = newloc;
6034 }
6035 p += length + 1;
6036 length = 0;
6037
6038 switch (c) {
6039 case '\0':
6040 goto breakloop;
6041 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006042 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 p--;
6044 continue;
6045 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006046 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006047 reject++;
6048 /* fall through */
6049 case ':':
6050 /*
6051 * sort of a hack - expand tildes in variable
6052 * assignments (after the first '=' and after ':'s).
6053 */
6054 if (*--p == '~') {
6055 goto tilde;
6056 }
6057 continue;
6058 }
6059
6060 switch (c) {
6061 case CTLENDVAR: /* ??? */
6062 goto breakloop;
6063 case CTLQUOTEMARK:
6064 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006065 if (!inquotes
6066 && memcmp(p, dolatstr, 4) == 0
6067 && ( p[4] == CTLQUOTEMARK
6068 || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK)
6069 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006070 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006071 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006072 goto start;
6073 }
6074 inquotes = !inquotes;
6075 addquote:
6076 if (quotes) {
6077 p--;
6078 length++;
6079 startloc++;
6080 }
6081 break;
6082 case CTLESC:
6083 startloc++;
6084 length++;
6085 goto addquote;
6086 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006087 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006088 goto start;
6089 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006090 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006091 case CTLBACKQ|CTLQUOTE:
6092 expbackq(argbackq->n, c, quotes);
6093 argbackq = argbackq->next;
6094 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006095#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006096 case CTLENDARI:
6097 p--;
6098 expari(quotes);
6099 goto start;
6100#endif
6101 }
6102 }
6103 breakloop:
6104 ;
6105}
6106
6107static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006108scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6109 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006110{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006111 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006112 char c;
6113
6114 loc = startp;
6115 loc2 = rmesc;
6116 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006117 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006118 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006119
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006120 c = *loc2;
6121 if (zero) {
6122 *loc2 = '\0';
6123 s = rmesc;
6124 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006125 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006126
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006127 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006128 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006129 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006130 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006131 loc++;
6132 loc++;
6133 loc2++;
6134 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006135 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006136}
6137
6138static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006139scanright(char *startp, char *rmesc, char *rmescend,
6140 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006141{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006142#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6143 int try2optimize = match_at_start;
6144#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006145 int esc = 0;
6146 char *loc;
6147 char *loc2;
6148
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006149 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6150 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6151 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6152 * Logic:
6153 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6154 * and on each iteration they go back two/one char until they reach the beginning.
6155 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6156 */
6157 /* TODO: document in what other circumstances we are called. */
6158
6159 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006160 int match;
6161 char c = *loc2;
6162 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006163 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006164 *loc2 = '\0';
6165 s = rmesc;
6166 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006167 match = pmatch(pattern, s);
6168 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006169 *loc2 = c;
6170 if (match)
6171 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006172#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6173 if (try2optimize) {
6174 /* Maybe we can optimize this:
6175 * if pattern ends with unescaped *, we can avoid checking
6176 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6177 * it wont match truncated "raw_value_of_" strings too.
6178 */
6179 unsigned plen = strlen(pattern);
6180 /* Does it end with "*"? */
6181 if (plen != 0 && pattern[--plen] == '*') {
6182 /* "xxxx*" is not escaped */
6183 /* "xxx\*" is escaped */
6184 /* "xx\\*" is not escaped */
6185 /* "x\\\*" is escaped */
6186 int slashes = 0;
6187 while (plen != 0 && pattern[--plen] == '\\')
6188 slashes++;
6189 if (!(slashes & 1))
6190 break; /* ends with unescaped "*" */
6191 }
6192 try2optimize = 0;
6193 }
6194#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006195 loc--;
6196 if (quotes) {
6197 if (--esc < 0) {
6198 esc = esclen(startp, loc);
6199 }
6200 if (esc % 2) {
6201 esc--;
6202 loc--;
6203 }
6204 }
6205 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006206 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006207}
6208
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006209static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006210static void
6211varunset(const char *end, const char *var, const char *umsg, int varflags)
6212{
6213 const char *msg;
6214 const char *tail;
6215
6216 tail = nullstr;
6217 msg = "parameter not set";
6218 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006219 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006220 if (varflags & VSNUL)
6221 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006222 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006223 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006224 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006225 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006226 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006227}
6228
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006229#if ENABLE_ASH_BASH_COMPAT
6230static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006231parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006232{
6233 char *idx, *repl = NULL;
6234 unsigned char c;
6235
Denys Vlasenko16149002010-08-06 22:06:21 +02006236 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006237 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006238 idx = arg;
6239 while (1) {
6240 c = *arg;
6241 if (!c)
6242 break;
6243 if (c == '/') {
6244 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006245 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006246 repl = idx + 1;
6247 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006248 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006249 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006250 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006251 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006252 /*
6253 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6254 * The result is a_\_z_c (not a\_\_z_c)!
6255 *
6256 * Enable debug prints in this function and you'll see:
6257 * ash: arg:'\\b/_\\_z_' varflags:d
6258 * ash: pattern:'\\b' repl:'_\_z_'
6259 * That is, \\b is interpreted as \\b, but \\_ as \_!
6260 * IOW: search pattern and replace string treat backslashes
6261 * differently! That is the reason why we check repl below:
6262 */
6263 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6264 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006265 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006266 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006267 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006268
6269 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006270}
6271#endif /* ENABLE_ASH_BASH_COMPAT */
6272
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006274subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006275 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006276{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006277 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006278 char *startp;
6279 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006280 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006281 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006282 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006283 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006284 int saveherefd = herefd;
6285 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006286 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006287 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006288
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006289 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d",
6290 // p, varname, strloc, subtype, startloc, varflags, quotes);
6291
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006292 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006293 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6294 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006295 STPUTC('\0', expdest);
6296 herefd = saveherefd;
6297 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006298 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006299
6300 switch (subtype) {
6301 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006302 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006303 amount = startp - expdest;
6304 STADJUST(amount, expdest);
6305 return startp;
6306
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006307 case VSQUESTION:
6308 varunset(p, varname, startp, varflags);
6309 /* NOTREACHED */
6310
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006311#if ENABLE_ASH_BASH_COMPAT
6312 case VSSUBSTR:
6313 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006314 /* Read POS in ${var:POS:LEN} */
6315 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006316 len = str - startp - 1;
6317
6318 /* *loc != '\0', guaranteed by parser */
6319 if (quotes) {
6320 char *ptr;
6321
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006322 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006323 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006324 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006325 len--;
6326 ptr++;
6327 }
6328 }
6329 }
6330 orig_len = len;
6331
6332 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006333 /* ${var::LEN} */
6334 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006335 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006336 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006337 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006338 while (*loc && *loc != ':') {
6339 /* TODO?
6340 * bash complains on: var=qwe; echo ${var:1a:123}
6341 if (!isdigit(*loc))
6342 ash_msg_and_raise_error(msg_illnum, str);
6343 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006344 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006345 }
6346 if (*loc++ == ':') {
6347 len = number(loc);
6348 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006349 }
6350 if (pos >= orig_len) {
6351 pos = 0;
6352 len = 0;
6353 }
6354 if (len > (orig_len - pos))
6355 len = orig_len - pos;
6356
6357 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006358 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006359 str++;
6360 }
6361 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006362 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006363 *loc++ = *str++;
6364 *loc++ = *str++;
6365 }
6366 *loc = '\0';
6367 amount = loc - expdest;
6368 STADJUST(amount, expdest);
6369 return loc;
6370#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006371 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006372
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006373 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006374
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006375 /* We'll comeback here if we grow the stack while handling
6376 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6377 * stack will need rebasing, and we'll need to remove our work
6378 * areas each time
6379 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006380 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006381
6382 amount = expdest - ((char *)stackblock() + resetloc);
6383 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006384 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006385
6386 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006387 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006388 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006389 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006390 if (rmesc != startp) {
6391 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006392 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006393 }
6394 }
6395 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006396 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006397 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006398 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006399
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006400#if ENABLE_ASH_BASH_COMPAT
6401 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006402 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006403
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006404 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006405 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006406 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006407 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006408 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006409 }
6410
6411 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006412 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006413 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006414
6415 len = 0;
6416 idx = startp;
6417 end = str - 1;
6418 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006419 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006420 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006421 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006422 if (!loc) {
6423 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006424 char *restart_detect = stackblock();
6425 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006426 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006427 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006428 idx++;
6429 len++;
6430 STPUTC(*idx, expdest);
6431 }
6432 if (stackblock() != restart_detect)
6433 goto restart;
6434 idx++;
6435 len++;
6436 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006437 /* continue; - prone to quadratic behavior, smarter code: */
6438 if (idx >= end)
6439 break;
6440 if (str[0] == '*') {
6441 /* Pattern is "*foo". If "*foo" does not match "long_string",
6442 * it would never match "ong_string" etc, no point in trying.
6443 */
6444 goto skip_matching;
6445 }
6446 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006447 }
6448
6449 if (subtype == VSREPLACEALL) {
6450 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006451 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006452 idx++;
6453 idx++;
6454 rmesc++;
6455 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006456 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006457 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006458 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006459
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006460 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006461 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006462 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006463 if (quotes && *loc == '\\') {
6464 STPUTC(CTLESC, expdest);
6465 len++;
6466 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006467 STPUTC(*loc, expdest);
6468 if (stackblock() != restart_detect)
6469 goto restart;
6470 len++;
6471 }
6472
6473 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006474 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006475 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006476 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006477 STPUTC(*idx, expdest);
6478 if (stackblock() != restart_detect)
6479 goto restart;
6480 len++;
6481 idx++;
6482 }
6483 break;
6484 }
6485 }
6486
6487 /* We've put the replaced text into a buffer at workloc, now
6488 * move it to the right place and adjust the stack.
6489 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006490 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006491 startp = (char *)stackblock() + startloc;
6492 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006493 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006494 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006495 STADJUST(-amount, expdest);
6496 return startp;
6497 }
6498#endif /* ENABLE_ASH_BASH_COMPAT */
6499
6500 subtype -= VSTRIMRIGHT;
6501#if DEBUG
6502 if (subtype < 0 || subtype > 7)
6503 abort();
6504#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006505 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006506 zero = subtype >> 1;
6507 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6508 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6509
6510 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6511 if (loc) {
6512 if (zero) {
6513 memmove(startp, loc, str - loc);
6514 loc = startp + (str - loc) - 1;
6515 }
6516 *loc = '\0';
6517 amount = loc - expdest;
6518 STADJUST(amount, expdest);
6519 }
6520 return loc;
6521}
6522
6523/*
6524 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006525 * name parameter (examples):
6526 * ash -c 'echo $1' name:'1='
6527 * ash -c 'echo $qwe' name:'qwe='
6528 * ash -c 'echo $$' name:'$='
6529 * ash -c 'echo ${$}' name:'$='
6530 * ash -c 'echo ${$##q}' name:'$=q'
6531 * ash -c 'echo ${#$}' name:'$='
6532 * note: examples with bad shell syntax:
6533 * ash -c 'echo ${#$1}' name:'$=1'
6534 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006535 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006536static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006537varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006538{
Mike Frysinger98c52642009-04-02 10:02:37 +00006539 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006540 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006541 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006542 int sepq = 0;
6543 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006545 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006546 int quoted = varflags & VSQUOTE;
6547 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006548
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006549 switch (*name) {
6550 case '$':
6551 num = rootpid;
6552 goto numvar;
6553 case '?':
6554 num = exitstatus;
6555 goto numvar;
6556 case '#':
6557 num = shellparam.nparam;
6558 goto numvar;
6559 case '!':
6560 num = backgndpid;
6561 if (num == 0)
6562 return -1;
6563 numvar:
6564 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006565 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006566 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006567 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006568 for (i = NOPTS - 1; i >= 0; i--) {
6569 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006570 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006571 len++;
6572 }
6573 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006574 check_1char_name:
6575#if 0
6576 /* handles cases similar to ${#$1} */
6577 if (name[2] != '\0')
6578 raise_error_syntax("bad substitution");
6579#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006580 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006581 case '@': {
6582 char **ap;
6583 int sep;
6584
6585 if (quoted && (flags & EXP_FULL)) {
6586 /* note: this is not meant as PEOF value */
6587 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006589 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006590 /* fall through */
6591 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006592 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006593 i = SIT(sep, syntax);
6594 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006595 sepq = 1;
6596 param:
6597 ap = shellparam.p;
6598 if (!ap)
6599 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006600 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006601 size_t partlen;
6602
6603 partlen = strlen(p);
6604 len += partlen;
6605
6606 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6607 memtodest(p, partlen, syntax, quotes);
6608
6609 if (*ap && sep) {
6610 char *q;
6611
6612 len++;
6613 if (subtype == VSPLUS || subtype == VSLENGTH) {
6614 continue;
6615 }
6616 q = expdest;
6617 if (sepq)
6618 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006619 /* note: may put NUL despite sep != 0
6620 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006621 STPUTC(sep, q);
6622 expdest = q;
6623 }
6624 }
6625 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006626 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006627 case '0':
6628 case '1':
6629 case '2':
6630 case '3':
6631 case '4':
6632 case '5':
6633 case '6':
6634 case '7':
6635 case '8':
6636 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006637 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006638 if (num < 0 || num > shellparam.nparam)
6639 return -1;
6640 p = num ? shellparam.p[num - 1] : arg0;
6641 goto value;
6642 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006643 /* NB: name has form "VAR=..." */
6644
6645 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6646 * which should be considered before we check variables. */
6647 if (var_str_list) {
6648 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6649 p = NULL;
6650 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006651 char *str, *eq;
6652 str = var_str_list->text;
6653 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006654 if (!eq) /* stop at first non-assignment */
6655 break;
6656 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006657 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006658 && strncmp(str, name, name_len) == 0
6659 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006660 p = eq;
6661 /* goto value; - WRONG! */
6662 /* think "A=1 A=2 B=$A" */
6663 }
6664 var_str_list = var_str_list->next;
6665 } while (var_str_list);
6666 if (p)
6667 goto value;
6668 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006669 p = lookupvar(name);
6670 value:
6671 if (!p)
6672 return -1;
6673
6674 len = strlen(p);
6675 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6676 memtodest(p, len, syntax, quotes);
6677 return len;
6678 }
6679
6680 if (subtype == VSPLUS || subtype == VSLENGTH)
6681 STADJUST(-len, expdest);
6682 return len;
6683}
6684
6685/*
6686 * Expand a variable, and return a pointer to the next character in the
6687 * input string.
6688 */
6689static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006690evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006691{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006692 char varflags;
6693 char subtype;
6694 char quoted;
6695 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006696 char *var;
6697 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006698 int startloc;
6699 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006700
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006701 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006702 subtype = varflags & VSTYPE;
6703 quoted = varflags & VSQUOTE;
6704 var = p;
6705 easy = (!quoted || (*var == '@' && shellparam.nparam));
6706 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006707 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006708
6709 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006710 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006711 if (varflags & VSNUL)
6712 varlen--;
6713
6714 if (subtype == VSPLUS) {
6715 varlen = -1 - varlen;
6716 goto vsplus;
6717 }
6718
6719 if (subtype == VSMINUS) {
6720 vsplus:
6721 if (varlen < 0) {
6722 argstr(
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006723 p, flags | EXP_TILDE |
6724 (quoted ? EXP_QWORD : EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006725 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006726 );
6727 goto end;
6728 }
6729 if (easy)
6730 goto record;
6731 goto end;
6732 }
6733
6734 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6735 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006736 if (subevalvar(p, var, /* strloc: */ 0,
6737 subtype, startloc, varflags,
6738 /* quotes: */ 0,
6739 var_str_list)
6740 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006741 varflags &= ~VSNUL;
6742 /*
6743 * Remove any recorded regions beyond
6744 * start of variable
6745 */
6746 removerecordregions(startloc);
6747 goto again;
6748 }
6749 goto end;
6750 }
6751 if (easy)
6752 goto record;
6753 goto end;
6754 }
6755
6756 if (varlen < 0 && uflag)
6757 varunset(p, var, 0, 0);
6758
6759 if (subtype == VSLENGTH) {
6760 cvtnum(varlen > 0 ? varlen : 0);
6761 goto record;
6762 }
6763
6764 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006765 if (easy)
6766 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006767 goto end;
6768 }
6769
6770#if DEBUG
6771 switch (subtype) {
6772 case VSTRIMLEFT:
6773 case VSTRIMLEFTMAX:
6774 case VSTRIMRIGHT:
6775 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006776#if ENABLE_ASH_BASH_COMPAT
6777 case VSSUBSTR:
6778 case VSREPLACE:
6779 case VSREPLACEALL:
6780#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006781 break;
6782 default:
6783 abort();
6784 }
6785#endif
6786
6787 if (varlen >= 0) {
6788 /*
6789 * Terminate the string and start recording the pattern
6790 * right after it
6791 */
6792 STPUTC('\0', expdest);
6793 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006794 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006795 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006796//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006797 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006798 var_str_list)
6799 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006800 int amount = expdest - (
6801 (char *)stackblock() + patloc - 1
6802 );
6803 STADJUST(-amount, expdest);
6804 }
6805 /* Remove any recorded regions beyond start of variable */
6806 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006807 record:
6808 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006809 }
6810
6811 end:
6812 if (subtype != VSNORMAL) { /* skip to end of alternative */
6813 int nesting = 1;
6814 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006815 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006816 if (c == CTLESC)
6817 p++;
6818 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6819 if (varlen >= 0)
6820 argbackq = argbackq->next;
6821 } else if (c == CTLVAR) {
6822 if ((*p++ & VSTYPE) != VSNORMAL)
6823 nesting++;
6824 } else if (c == CTLENDVAR) {
6825 if (--nesting == 0)
6826 break;
6827 }
6828 }
6829 }
6830 return p;
6831}
6832
6833/*
6834 * Break the argument string into pieces based upon IFS and add the
6835 * strings to the argument list. The regions of the string to be
6836 * searched for IFS characters have been stored by recordregion.
6837 */
6838static void
6839ifsbreakup(char *string, struct arglist *arglist)
6840{
6841 struct ifsregion *ifsp;
6842 struct strlist *sp;
6843 char *start;
6844 char *p;
6845 char *q;
6846 const char *ifs, *realifs;
6847 int ifsspc;
6848 int nulonly;
6849
6850 start = string;
6851 if (ifslastp != NULL) {
6852 ifsspc = 0;
6853 nulonly = 0;
6854 realifs = ifsset() ? ifsval() : defifs;
6855 ifsp = &ifsfirst;
6856 do {
6857 p = string + ifsp->begoff;
6858 nulonly = ifsp->nulonly;
6859 ifs = nulonly ? nullstr : realifs;
6860 ifsspc = 0;
6861 while (p < string + ifsp->endoff) {
6862 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006863 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006864 p++;
6865 if (!strchr(ifs, *p)) {
6866 p++;
6867 continue;
6868 }
6869 if (!nulonly)
6870 ifsspc = (strchr(defifs, *p) != NULL);
6871 /* Ignore IFS whitespace at start */
6872 if (q == start && ifsspc) {
6873 p++;
6874 start = p;
6875 continue;
6876 }
6877 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006878 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006879 sp->text = start;
6880 *arglist->lastp = sp;
6881 arglist->lastp = &sp->next;
6882 p++;
6883 if (!nulonly) {
6884 for (;;) {
6885 if (p >= string + ifsp->endoff) {
6886 break;
6887 }
6888 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006889 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006890 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006891 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006892 p = q;
6893 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006894 }
6895 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006896 if (ifsspc) {
6897 p++;
6898 ifsspc = 0;
6899 } else {
6900 p = q;
6901 break;
6902 }
6903 } else
6904 p++;
6905 }
6906 }
6907 start = p;
6908 } /* while */
6909 ifsp = ifsp->next;
6910 } while (ifsp != NULL);
6911 if (nulonly)
6912 goto add;
6913 }
6914
6915 if (!*start)
6916 return;
6917
6918 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006919 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006920 sp->text = start;
6921 *arglist->lastp = sp;
6922 arglist->lastp = &sp->next;
6923}
6924
6925static void
6926ifsfree(void)
6927{
6928 struct ifsregion *p;
6929
6930 INT_OFF;
6931 p = ifsfirst.next;
6932 do {
6933 struct ifsregion *ifsp;
6934 ifsp = p->next;
6935 free(p);
6936 p = ifsp;
6937 } while (p);
6938 ifslastp = NULL;
6939 ifsfirst.next = NULL;
6940 INT_ON;
6941}
6942
6943/*
6944 * Add a file name to the list.
6945 */
6946static void
6947addfname(const char *name)
6948{
6949 struct strlist *sp;
6950
Denis Vlasenko597906c2008-02-20 16:38:54 +00006951 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006952 sp->text = ststrdup(name);
6953 *exparg.lastp = sp;
6954 exparg.lastp = &sp->next;
6955}
6956
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006957/*
6958 * Do metacharacter (i.e. *, ?, [...]) expansion.
6959 */
6960static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006961expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006962{
6963 char *p;
6964 const char *cp;
6965 char *start;
6966 char *endname;
6967 int metaflag;
6968 struct stat statb;
6969 DIR *dirp;
6970 struct dirent *dp;
6971 int atend;
6972 int matchdot;
6973
6974 metaflag = 0;
6975 start = name;
6976 for (p = name; *p; p++) {
6977 if (*p == '*' || *p == '?')
6978 metaflag = 1;
6979 else if (*p == '[') {
6980 char *q = p + 1;
6981 if (*q == '!')
6982 q++;
6983 for (;;) {
6984 if (*q == '\\')
6985 q++;
6986 if (*q == '/' || *q == '\0')
6987 break;
6988 if (*++q == ']') {
6989 metaflag = 1;
6990 break;
6991 }
6992 }
6993 } else if (*p == '\\')
6994 p++;
6995 else if (*p == '/') {
6996 if (metaflag)
6997 goto out;
6998 start = p + 1;
6999 }
7000 }
7001 out:
7002 if (metaflag == 0) { /* we've reached the end of the file name */
7003 if (enddir != expdir)
7004 metaflag++;
7005 p = name;
7006 do {
7007 if (*p == '\\')
7008 p++;
7009 *enddir++ = *p;
7010 } while (*p++);
7011 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7012 addfname(expdir);
7013 return;
7014 }
7015 endname = p;
7016 if (name < start) {
7017 p = name;
7018 do {
7019 if (*p == '\\')
7020 p++;
7021 *enddir++ = *p++;
7022 } while (p < start);
7023 }
7024 if (enddir == expdir) {
7025 cp = ".";
7026 } else if (enddir == expdir + 1 && *expdir == '/') {
7027 cp = "/";
7028 } else {
7029 cp = expdir;
7030 enddir[-1] = '\0';
7031 }
7032 dirp = opendir(cp);
7033 if (dirp == NULL)
7034 return;
7035 if (enddir != expdir)
7036 enddir[-1] = '/';
7037 if (*endname == 0) {
7038 atend = 1;
7039 } else {
7040 atend = 0;
7041 *endname++ = '\0';
7042 }
7043 matchdot = 0;
7044 p = start;
7045 if (*p == '\\')
7046 p++;
7047 if (*p == '.')
7048 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007049 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007050 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007051 continue;
7052 if (pmatch(start, dp->d_name)) {
7053 if (atend) {
7054 strcpy(enddir, dp->d_name);
7055 addfname(expdir);
7056 } else {
7057 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7058 continue;
7059 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007060 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007061 }
7062 }
7063 }
7064 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007065 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007066 endname[-1] = '/';
7067}
7068
7069static struct strlist *
7070msort(struct strlist *list, int len)
7071{
7072 struct strlist *p, *q = NULL;
7073 struct strlist **lpp;
7074 int half;
7075 int n;
7076
7077 if (len <= 1)
7078 return list;
7079 half = len >> 1;
7080 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007081 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007082 q = p;
7083 p = p->next;
7084 }
7085 q->next = NULL; /* terminate first half of list */
7086 q = msort(list, half); /* sort first half of list */
7087 p = msort(p, len - half); /* sort second half */
7088 lpp = &list;
7089 for (;;) {
7090#if ENABLE_LOCALE_SUPPORT
7091 if (strcoll(p->text, q->text) < 0)
7092#else
7093 if (strcmp(p->text, q->text) < 0)
7094#endif
7095 {
7096 *lpp = p;
7097 lpp = &p->next;
7098 p = *lpp;
7099 if (p == NULL) {
7100 *lpp = q;
7101 break;
7102 }
7103 } else {
7104 *lpp = q;
7105 lpp = &q->next;
7106 q = *lpp;
7107 if (q == NULL) {
7108 *lpp = p;
7109 break;
7110 }
7111 }
7112 }
7113 return list;
7114}
7115
7116/*
7117 * Sort the results of file name expansion. It calculates the number of
7118 * strings to sort and then calls msort (short for merge sort) to do the
7119 * work.
7120 */
7121static struct strlist *
7122expsort(struct strlist *str)
7123{
7124 int len;
7125 struct strlist *sp;
7126
7127 len = 0;
7128 for (sp = str; sp; sp = sp->next)
7129 len++;
7130 return msort(str, len);
7131}
7132
7133static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007134expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007135{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007136 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007137 '*', '?', '[', 0
7138 };
7139 /* TODO - EXP_REDIR */
7140
7141 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007142 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007143 struct strlist **savelastp;
7144 struct strlist *sp;
7145 char *p;
7146
7147 if (fflag)
7148 goto nometa;
7149 if (!strpbrk(str->text, metachars))
7150 goto nometa;
7151 savelastp = exparg.lastp;
7152
7153 INT_OFF;
7154 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7155 {
7156 int i = strlen(str->text);
7157 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7158 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007159 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007160 free(expdir);
7161 if (p != str->text)
7162 free(p);
7163 INT_ON;
7164 if (exparg.lastp == savelastp) {
7165 /*
7166 * no matches
7167 */
7168 nometa:
7169 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007170 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007171 exparg.lastp = &str->next;
7172 } else {
7173 *exparg.lastp = NULL;
7174 *savelastp = sp = expsort(*savelastp);
7175 while (sp->next != NULL)
7176 sp = sp->next;
7177 exparg.lastp = &sp->next;
7178 }
7179 str = str->next;
7180 }
7181}
7182
7183/*
7184 * Perform variable substitution and command substitution on an argument,
7185 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7186 * perform splitting and file name expansion. When arglist is NULL, perform
7187 * here document expansion.
7188 */
7189static void
7190expandarg(union node *arg, struct arglist *arglist, int flag)
7191{
7192 struct strlist *sp;
7193 char *p;
7194
7195 argbackq = arg->narg.backquote;
7196 STARTSTACKSTR(expdest);
7197 ifsfirst.next = NULL;
7198 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007199 argstr(arg->narg.text, flag,
7200 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007201 p = _STPUTC('\0', expdest);
7202 expdest = p - 1;
7203 if (arglist == NULL) {
7204 return; /* here document expanded */
7205 }
7206 p = grabstackstr(p);
7207 exparg.lastp = &exparg.list;
7208 /*
7209 * TODO - EXP_REDIR
7210 */
7211 if (flag & EXP_FULL) {
7212 ifsbreakup(p, &exparg);
7213 *exparg.lastp = NULL;
7214 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007215 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007216 } else {
7217 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007218 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007219 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007220 sp->text = p;
7221 *exparg.lastp = sp;
7222 exparg.lastp = &sp->next;
7223 }
7224 if (ifsfirst.next)
7225 ifsfree();
7226 *exparg.lastp = NULL;
7227 if (exparg.list) {
7228 *arglist->lastp = exparg.list;
7229 arglist->lastp = exparg.lastp;
7230 }
7231}
7232
7233/*
7234 * Expand shell variables and backquotes inside a here document.
7235 */
7236static void
7237expandhere(union node *arg, int fd)
7238{
7239 herefd = fd;
7240 expandarg(arg, (struct arglist *)NULL, 0);
7241 full_write(fd, stackblock(), expdest - (char *)stackblock());
7242}
7243
7244/*
7245 * Returns true if the pattern matches the string.
7246 */
7247static int
7248patmatch(char *pattern, const char *string)
7249{
7250 return pmatch(preglob(pattern, 0, 0), string);
7251}
7252
7253/*
7254 * See if a pattern matches in a case statement.
7255 */
7256static int
7257casematch(union node *pattern, char *val)
7258{
7259 struct stackmark smark;
7260 int result;
7261
7262 setstackmark(&smark);
7263 argbackq = pattern->narg.backquote;
7264 STARTSTACKSTR(expdest);
7265 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007266 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7267 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007268 STACKSTRNUL(expdest);
7269 result = patmatch(stackblock(), val);
7270 popstackmark(&smark);
7271 return result;
7272}
7273
7274
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007275/* ============ find_command */
7276
7277struct builtincmd {
7278 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007279 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007280 /* unsigned flags; */
7281};
7282#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007283/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007284 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007285#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007286#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287
7288struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007289 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007290 union param {
7291 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007292 /* index >= 0 for commands without path (slashes) */
7293 /* (TODO: what exactly does the value mean? PATH position?) */
7294 /* index == -1 for commands with slashes */
7295 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007296 const struct builtincmd *cmd;
7297 struct funcnode *func;
7298 } u;
7299};
7300/* values of cmdtype */
7301#define CMDUNKNOWN -1 /* no entry in table for command */
7302#define CMDNORMAL 0 /* command is an executable program */
7303#define CMDFUNCTION 1 /* command is a shell function */
7304#define CMDBUILTIN 2 /* command is a shell builtin */
7305
7306/* action to find_command() */
7307#define DO_ERR 0x01 /* prints errors */
7308#define DO_ABS 0x02 /* checks absolute paths */
7309#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7310#define DO_ALTPATH 0x08 /* using alternate path */
7311#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7312
7313static void find_command(char *, struct cmdentry *, int, const char *);
7314
7315
7316/* ============ Hashing commands */
7317
7318/*
7319 * When commands are first encountered, they are entered in a hash table.
7320 * This ensures that a full path search will not have to be done for them
7321 * on each invocation.
7322 *
7323 * We should investigate converting to a linear search, even though that
7324 * would make the command name "hash" a misnomer.
7325 */
7326
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007327struct tblentry {
7328 struct tblentry *next; /* next entry in hash chain */
7329 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007330 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007331 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007332 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333};
7334
Denis Vlasenko01631112007-12-16 17:20:38 +00007335static struct tblentry **cmdtable;
7336#define INIT_G_cmdtable() do { \
7337 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7338} while (0)
7339
7340static int builtinloc = -1; /* index in path of %builtin, or -1 */
7341
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007342
7343static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007344tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007345{
7346 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007347
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007348#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007349 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007350 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007351 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007352 while (*envp)
7353 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007354 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007355 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007356 /* re-exec ourselves with the new arguments */
7357 execve(bb_busybox_exec_path, argv, envp);
7358 /* If they called chroot or otherwise made the binary no longer
7359 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007360 }
7361#endif
7362
7363 repeat:
7364#ifdef SYSV
7365 do {
7366 execve(cmd, argv, envp);
7367 } while (errno == EINTR);
7368#else
7369 execve(cmd, argv, envp);
7370#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007371 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007372 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007373 return;
7374 }
7375 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007376 char **ap;
7377 char **new;
7378
7379 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007380 continue;
7381 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007382 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007383 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007384 ap += 2;
7385 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007386 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007387 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007388 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007389 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390 goto repeat;
7391 }
7392}
7393
7394/*
7395 * Exec a program. Never returns. If you change this routine, you may
7396 * have to change the find_command routine as well.
7397 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007398static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007399static void
7400shellexec(char **argv, const char *path, int idx)
7401{
7402 char *cmdname;
7403 int e;
7404 char **envp;
7405 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007406#if ENABLE_FEATURE_SH_STANDALONE
7407 int applet_no = -1;
7408#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007409
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007410 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007411 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007412 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007413#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007414 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007415#endif
7416 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007417 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007418 e = errno;
7419 } else {
7420 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007421 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007422 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007423 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007424 if (errno != ENOENT && errno != ENOTDIR)
7425 e = errno;
7426 }
7427 stunalloc(cmdname);
7428 }
7429 }
7430
7431 /* Map to POSIX errors */
7432 switch (e) {
7433 case EACCES:
7434 exerrno = 126;
7435 break;
7436 case ENOENT:
7437 exerrno = 127;
7438 break;
7439 default:
7440 exerrno = 2;
7441 break;
7442 }
7443 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007444 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7445 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007446 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7447 /* NOTREACHED */
7448}
7449
7450static void
7451printentry(struct tblentry *cmdp)
7452{
7453 int idx;
7454 const char *path;
7455 char *name;
7456
7457 idx = cmdp->param.index;
7458 path = pathval();
7459 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007460 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007461 stunalloc(name);
7462 } while (--idx >= 0);
7463 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7464}
7465
7466/*
7467 * Clear out command entries. The argument specifies the first entry in
7468 * PATH which has changed.
7469 */
7470static void
7471clearcmdentry(int firstchange)
7472{
7473 struct tblentry **tblp;
7474 struct tblentry **pp;
7475 struct tblentry *cmdp;
7476
7477 INT_OFF;
7478 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7479 pp = tblp;
7480 while ((cmdp = *pp) != NULL) {
7481 if ((cmdp->cmdtype == CMDNORMAL &&
7482 cmdp->param.index >= firstchange)
7483 || (cmdp->cmdtype == CMDBUILTIN &&
7484 builtinloc >= firstchange)
7485 ) {
7486 *pp = cmdp->next;
7487 free(cmdp);
7488 } else {
7489 pp = &cmdp->next;
7490 }
7491 }
7492 }
7493 INT_ON;
7494}
7495
7496/*
7497 * Locate a command in the command hash table. If "add" is nonzero,
7498 * add the command to the table if it is not already present. The
7499 * variable "lastcmdentry" is set to point to the address of the link
7500 * pointing to the entry, so that delete_cmd_entry can delete the
7501 * entry.
7502 *
7503 * Interrupts must be off if called with add != 0.
7504 */
7505static struct tblentry **lastcmdentry;
7506
7507static struct tblentry *
7508cmdlookup(const char *name, int add)
7509{
7510 unsigned int hashval;
7511 const char *p;
7512 struct tblentry *cmdp;
7513 struct tblentry **pp;
7514
7515 p = name;
7516 hashval = (unsigned char)*p << 4;
7517 while (*p)
7518 hashval += (unsigned char)*p++;
7519 hashval &= 0x7FFF;
7520 pp = &cmdtable[hashval % CMDTABLESIZE];
7521 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7522 if (strcmp(cmdp->cmdname, name) == 0)
7523 break;
7524 pp = &cmdp->next;
7525 }
7526 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007527 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7528 + strlen(name)
7529 /* + 1 - already done because
7530 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007531 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007532 cmdp->cmdtype = CMDUNKNOWN;
7533 strcpy(cmdp->cmdname, name);
7534 }
7535 lastcmdentry = pp;
7536 return cmdp;
7537}
7538
7539/*
7540 * Delete the command entry returned on the last lookup.
7541 */
7542static void
7543delete_cmd_entry(void)
7544{
7545 struct tblentry *cmdp;
7546
7547 INT_OFF;
7548 cmdp = *lastcmdentry;
7549 *lastcmdentry = cmdp->next;
7550 if (cmdp->cmdtype == CMDFUNCTION)
7551 freefunc(cmdp->param.func);
7552 free(cmdp);
7553 INT_ON;
7554}
7555
7556/*
7557 * Add a new command entry, replacing any existing command entry for
7558 * the same name - except special builtins.
7559 */
7560static void
7561addcmdentry(char *name, struct cmdentry *entry)
7562{
7563 struct tblentry *cmdp;
7564
7565 cmdp = cmdlookup(name, 1);
7566 if (cmdp->cmdtype == CMDFUNCTION) {
7567 freefunc(cmdp->param.func);
7568 }
7569 cmdp->cmdtype = entry->cmdtype;
7570 cmdp->param = entry->u;
7571 cmdp->rehash = 0;
7572}
7573
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007574static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007575hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007576{
7577 struct tblentry **pp;
7578 struct tblentry *cmdp;
7579 int c;
7580 struct cmdentry entry;
7581 char *name;
7582
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007583 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007584 clearcmdentry(0);
7585 return 0;
7586 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007587
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007588 if (*argptr == NULL) {
7589 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7590 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7591 if (cmdp->cmdtype == CMDNORMAL)
7592 printentry(cmdp);
7593 }
7594 }
7595 return 0;
7596 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007597
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007598 c = 0;
7599 while ((name = *argptr) != NULL) {
7600 cmdp = cmdlookup(name, 0);
7601 if (cmdp != NULL
7602 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007603 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7604 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007605 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007606 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007607 find_command(name, &entry, DO_ERR, pathval());
7608 if (entry.cmdtype == CMDUNKNOWN)
7609 c = 1;
7610 argptr++;
7611 }
7612 return c;
7613}
7614
7615/*
7616 * Called when a cd is done. Marks all commands so the next time they
7617 * are executed they will be rehashed.
7618 */
7619static void
7620hashcd(void)
7621{
7622 struct tblentry **pp;
7623 struct tblentry *cmdp;
7624
7625 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7626 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007627 if (cmdp->cmdtype == CMDNORMAL
7628 || (cmdp->cmdtype == CMDBUILTIN
7629 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7630 && builtinloc > 0)
7631 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007632 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007633 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007634 }
7635 }
7636}
7637
7638/*
7639 * Fix command hash table when PATH changed.
7640 * Called before PATH is changed. The argument is the new value of PATH;
7641 * pathval() still returns the old value at this point.
7642 * Called with interrupts off.
7643 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007644static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007645changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007646{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007647 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007648 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007649 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007650 int idx_bltin;
7651
7652 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007653 firstchange = 9999; /* assume no change */
7654 idx = 0;
7655 idx_bltin = -1;
7656 for (;;) {
7657 if (*old != *new) {
7658 firstchange = idx;
7659 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007660 || (*old == ':' && *new == '\0')
7661 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007663 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007664 old = new; /* ignore subsequent differences */
7665 }
7666 if (*new == '\0')
7667 break;
7668 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7669 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007670 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007671 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007672 new++;
7673 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007674 }
7675 if (builtinloc < 0 && idx_bltin >= 0)
7676 builtinloc = idx_bltin; /* zap builtins */
7677 if (builtinloc >= 0 && idx_bltin < 0)
7678 firstchange = 0;
7679 clearcmdentry(firstchange);
7680 builtinloc = idx_bltin;
7681}
7682
7683#define TEOF 0
7684#define TNL 1
7685#define TREDIR 2
7686#define TWORD 3
7687#define TSEMI 4
7688#define TBACKGND 5
7689#define TAND 6
7690#define TOR 7
7691#define TPIPE 8
7692#define TLP 9
7693#define TRP 10
7694#define TENDCASE 11
7695#define TENDBQUOTE 12
7696#define TNOT 13
7697#define TCASE 14
7698#define TDO 15
7699#define TDONE 16
7700#define TELIF 17
7701#define TELSE 18
7702#define TESAC 19
7703#define TFI 20
7704#define TFOR 21
7705#define TIF 22
7706#define TIN 23
7707#define TTHEN 24
7708#define TUNTIL 25
7709#define TWHILE 26
7710#define TBEGIN 27
7711#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007712typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007713
7714/* first char is indicating which tokens mark the end of a list */
7715static const char *const tokname_array[] = {
7716 "\1end of file",
7717 "\0newline",
7718 "\0redirection",
7719 "\0word",
7720 "\0;",
7721 "\0&",
7722 "\0&&",
7723 "\0||",
7724 "\0|",
7725 "\0(",
7726 "\1)",
7727 "\1;;",
7728 "\1`",
7729#define KWDOFFSET 13
7730 /* the following are keywords */
7731 "\0!",
7732 "\0case",
7733 "\1do",
7734 "\1done",
7735 "\1elif",
7736 "\1else",
7737 "\1esac",
7738 "\1fi",
7739 "\0for",
7740 "\0if",
7741 "\0in",
7742 "\1then",
7743 "\0until",
7744 "\0while",
7745 "\0{",
7746 "\1}",
7747};
7748
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007749/* Wrapper around strcmp for qsort/bsearch/... */
7750static int
7751pstrcmp(const void *a, const void *b)
7752{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007753 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007754}
7755
7756static const char *const *
7757findkwd(const char *s)
7758{
7759 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007760 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7761 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007762}
7763
7764/*
7765 * Locate and print what a word is...
7766 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007767static int
7768describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007769{
7770 struct cmdentry entry;
7771 struct tblentry *cmdp;
7772#if ENABLE_ASH_ALIAS
7773 const struct alias *ap;
7774#endif
7775 const char *path = pathval();
7776
7777 if (describe_command_verbose) {
7778 out1str(command);
7779 }
7780
7781 /* First look at the keywords */
7782 if (findkwd(command)) {
7783 out1str(describe_command_verbose ? " is a shell keyword" : command);
7784 goto out;
7785 }
7786
7787#if ENABLE_ASH_ALIAS
7788 /* Then look at the aliases */
7789 ap = lookupalias(command, 0);
7790 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007791 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007792 out1str("alias ");
7793 printalias(ap);
7794 return 0;
7795 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007796 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007797 goto out;
7798 }
7799#endif
7800 /* Then check if it is a tracked alias */
7801 cmdp = cmdlookup(command, 0);
7802 if (cmdp != NULL) {
7803 entry.cmdtype = cmdp->cmdtype;
7804 entry.u = cmdp->param;
7805 } else {
7806 /* Finally use brute force */
7807 find_command(command, &entry, DO_ABS, path);
7808 }
7809
7810 switch (entry.cmdtype) {
7811 case CMDNORMAL: {
7812 int j = entry.u.index;
7813 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007814 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007815 p = command;
7816 } else {
7817 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007818 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007819 stunalloc(p);
7820 } while (--j >= 0);
7821 }
7822 if (describe_command_verbose) {
7823 out1fmt(" is%s %s",
7824 (cmdp ? " a tracked alias for" : nullstr), p
7825 );
7826 } else {
7827 out1str(p);
7828 }
7829 break;
7830 }
7831
7832 case CMDFUNCTION:
7833 if (describe_command_verbose) {
7834 out1str(" is a shell function");
7835 } else {
7836 out1str(command);
7837 }
7838 break;
7839
7840 case CMDBUILTIN:
7841 if (describe_command_verbose) {
7842 out1fmt(" is a %sshell builtin",
7843 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7844 "special " : nullstr
7845 );
7846 } else {
7847 out1str(command);
7848 }
7849 break;
7850
7851 default:
7852 if (describe_command_verbose) {
7853 out1str(": not found\n");
7854 }
7855 return 127;
7856 }
7857 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007858 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007859 return 0;
7860}
7861
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007862static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007863typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007864{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007865 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007866 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007867 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007868
Denis Vlasenko46846e22007-05-20 13:08:31 +00007869 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007870 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007871 i++;
7872 verbose = 0;
7873 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007874 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007875 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007876 }
7877 return err;
7878}
7879
7880#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007881static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007882commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007883{
7884 int c;
7885 enum {
7886 VERIFY_BRIEF = 1,
7887 VERIFY_VERBOSE = 2,
7888 } verify = 0;
7889
7890 while ((c = nextopt("pvV")) != '\0')
7891 if (c == 'V')
7892 verify |= VERIFY_VERBOSE;
7893 else if (c == 'v')
7894 verify |= VERIFY_BRIEF;
7895#if DEBUG
7896 else if (c != 'p')
7897 abort();
7898#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007899 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7900 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007901 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007902 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007903
7904 return 0;
7905}
7906#endif
7907
7908
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007909/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007910
Denis Vlasenko340299a2008-11-21 10:36:36 +00007911static int funcblocksize; /* size of structures in function */
7912static int funcstringsize; /* size of strings in node */
7913static void *funcblock; /* block to allocate function from */
7914static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007915
Eric Andersencb57d552001-06-28 07:25:16 +00007916/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007917#define EV_EXIT 01 /* exit after evaluating tree */
7918#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007919#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007920
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007921static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007922 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7923 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7924 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7925 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7926 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7927 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7928 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7929 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7930 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7931 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7932 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7933 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7934 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7935 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7936 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7937 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7938 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007939#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007940 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007941#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007942 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7943 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7944 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7945 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7946 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7947 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7948 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7949 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7950 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007951};
7952
7953static void calcsize(union node *n);
7954
7955static void
7956sizenodelist(struct nodelist *lp)
7957{
7958 while (lp) {
7959 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7960 calcsize(lp->n);
7961 lp = lp->next;
7962 }
7963}
7964
7965static void
7966calcsize(union node *n)
7967{
7968 if (n == NULL)
7969 return;
7970 funcblocksize += nodesize[n->type];
7971 switch (n->type) {
7972 case NCMD:
7973 calcsize(n->ncmd.redirect);
7974 calcsize(n->ncmd.args);
7975 calcsize(n->ncmd.assign);
7976 break;
7977 case NPIPE:
7978 sizenodelist(n->npipe.cmdlist);
7979 break;
7980 case NREDIR:
7981 case NBACKGND:
7982 case NSUBSHELL:
7983 calcsize(n->nredir.redirect);
7984 calcsize(n->nredir.n);
7985 break;
7986 case NAND:
7987 case NOR:
7988 case NSEMI:
7989 case NWHILE:
7990 case NUNTIL:
7991 calcsize(n->nbinary.ch2);
7992 calcsize(n->nbinary.ch1);
7993 break;
7994 case NIF:
7995 calcsize(n->nif.elsepart);
7996 calcsize(n->nif.ifpart);
7997 calcsize(n->nif.test);
7998 break;
7999 case NFOR:
8000 funcstringsize += strlen(n->nfor.var) + 1;
8001 calcsize(n->nfor.body);
8002 calcsize(n->nfor.args);
8003 break;
8004 case NCASE:
8005 calcsize(n->ncase.cases);
8006 calcsize(n->ncase.expr);
8007 break;
8008 case NCLIST:
8009 calcsize(n->nclist.body);
8010 calcsize(n->nclist.pattern);
8011 calcsize(n->nclist.next);
8012 break;
8013 case NDEFUN:
8014 case NARG:
8015 sizenodelist(n->narg.backquote);
8016 funcstringsize += strlen(n->narg.text) + 1;
8017 calcsize(n->narg.next);
8018 break;
8019 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008020#if ENABLE_ASH_BASH_COMPAT
8021 case NTO2:
8022#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008023 case NCLOBBER:
8024 case NFROM:
8025 case NFROMTO:
8026 case NAPPEND:
8027 calcsize(n->nfile.fname);
8028 calcsize(n->nfile.next);
8029 break;
8030 case NTOFD:
8031 case NFROMFD:
8032 calcsize(n->ndup.vname);
8033 calcsize(n->ndup.next);
8034 break;
8035 case NHERE:
8036 case NXHERE:
8037 calcsize(n->nhere.doc);
8038 calcsize(n->nhere.next);
8039 break;
8040 case NNOT:
8041 calcsize(n->nnot.com);
8042 break;
8043 };
8044}
8045
8046static char *
8047nodeckstrdup(char *s)
8048{
8049 char *rtn = funcstring;
8050
8051 strcpy(funcstring, s);
8052 funcstring += strlen(s) + 1;
8053 return rtn;
8054}
8055
8056static union node *copynode(union node *);
8057
8058static struct nodelist *
8059copynodelist(struct nodelist *lp)
8060{
8061 struct nodelist *start;
8062 struct nodelist **lpp;
8063
8064 lpp = &start;
8065 while (lp) {
8066 *lpp = funcblock;
8067 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8068 (*lpp)->n = copynode(lp->n);
8069 lp = lp->next;
8070 lpp = &(*lpp)->next;
8071 }
8072 *lpp = NULL;
8073 return start;
8074}
8075
8076static union node *
8077copynode(union node *n)
8078{
8079 union node *new;
8080
8081 if (n == NULL)
8082 return NULL;
8083 new = funcblock;
8084 funcblock = (char *) funcblock + nodesize[n->type];
8085
8086 switch (n->type) {
8087 case NCMD:
8088 new->ncmd.redirect = copynode(n->ncmd.redirect);
8089 new->ncmd.args = copynode(n->ncmd.args);
8090 new->ncmd.assign = copynode(n->ncmd.assign);
8091 break;
8092 case NPIPE:
8093 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008094 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008095 break;
8096 case NREDIR:
8097 case NBACKGND:
8098 case NSUBSHELL:
8099 new->nredir.redirect = copynode(n->nredir.redirect);
8100 new->nredir.n = copynode(n->nredir.n);
8101 break;
8102 case NAND:
8103 case NOR:
8104 case NSEMI:
8105 case NWHILE:
8106 case NUNTIL:
8107 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8108 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8109 break;
8110 case NIF:
8111 new->nif.elsepart = copynode(n->nif.elsepart);
8112 new->nif.ifpart = copynode(n->nif.ifpart);
8113 new->nif.test = copynode(n->nif.test);
8114 break;
8115 case NFOR:
8116 new->nfor.var = nodeckstrdup(n->nfor.var);
8117 new->nfor.body = copynode(n->nfor.body);
8118 new->nfor.args = copynode(n->nfor.args);
8119 break;
8120 case NCASE:
8121 new->ncase.cases = copynode(n->ncase.cases);
8122 new->ncase.expr = copynode(n->ncase.expr);
8123 break;
8124 case NCLIST:
8125 new->nclist.body = copynode(n->nclist.body);
8126 new->nclist.pattern = copynode(n->nclist.pattern);
8127 new->nclist.next = copynode(n->nclist.next);
8128 break;
8129 case NDEFUN:
8130 case NARG:
8131 new->narg.backquote = copynodelist(n->narg.backquote);
8132 new->narg.text = nodeckstrdup(n->narg.text);
8133 new->narg.next = copynode(n->narg.next);
8134 break;
8135 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008136#if ENABLE_ASH_BASH_COMPAT
8137 case NTO2:
8138#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008139 case NCLOBBER:
8140 case NFROM:
8141 case NFROMTO:
8142 case NAPPEND:
8143 new->nfile.fname = copynode(n->nfile.fname);
8144 new->nfile.fd = n->nfile.fd;
8145 new->nfile.next = copynode(n->nfile.next);
8146 break;
8147 case NTOFD:
8148 case NFROMFD:
8149 new->ndup.vname = copynode(n->ndup.vname);
8150 new->ndup.dupfd = n->ndup.dupfd;
8151 new->ndup.fd = n->ndup.fd;
8152 new->ndup.next = copynode(n->ndup.next);
8153 break;
8154 case NHERE:
8155 case NXHERE:
8156 new->nhere.doc = copynode(n->nhere.doc);
8157 new->nhere.fd = n->nhere.fd;
8158 new->nhere.next = copynode(n->nhere.next);
8159 break;
8160 case NNOT:
8161 new->nnot.com = copynode(n->nnot.com);
8162 break;
8163 };
8164 new->type = n->type;
8165 return new;
8166}
8167
8168/*
8169 * Make a copy of a parse tree.
8170 */
8171static struct funcnode *
8172copyfunc(union node *n)
8173{
8174 struct funcnode *f;
8175 size_t blocksize;
8176
8177 funcblocksize = offsetof(struct funcnode, n);
8178 funcstringsize = 0;
8179 calcsize(n);
8180 blocksize = funcblocksize;
8181 f = ckmalloc(blocksize + funcstringsize);
8182 funcblock = (char *) f + offsetof(struct funcnode, n);
8183 funcstring = (char *) f + blocksize;
8184 copynode(n);
8185 f->count = 0;
8186 return f;
8187}
8188
8189/*
8190 * Define a shell function.
8191 */
8192static void
8193defun(char *name, union node *func)
8194{
8195 struct cmdentry entry;
8196
8197 INT_OFF;
8198 entry.cmdtype = CMDFUNCTION;
8199 entry.u.func = copyfunc(func);
8200 addcmdentry(name, &entry);
8201 INT_ON;
8202}
8203
Denis Vlasenko4b875702009-03-19 13:30:04 +00008204/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008205#define SKIPBREAK (1 << 0)
8206#define SKIPCONT (1 << 1)
8207#define SKIPFUNC (1 << 2)
8208#define SKIPFILE (1 << 3)
8209#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008210static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008211static int skipcount; /* number of levels to skip */
8212static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008213static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008214
Denis Vlasenko4b875702009-03-19 13:30:04 +00008215/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008216static int evalstring(char *s, int mask);
8217
Denis Vlasenko4b875702009-03-19 13:30:04 +00008218/* Called to execute a trap.
8219 * Single callsite - at the end of evaltree().
8220 * If we return non-zero, exaltree raises EXEXIT exception.
8221 *
8222 * Perhaps we should avoid entering new trap handlers
8223 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008224 */
8225static int
8226dotrap(void)
8227{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008228 uint8_t *g;
8229 int sig;
8230 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008231
8232 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008233 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008234 xbarrier();
8235
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008236 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008237 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8238 int want_exexit;
8239 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008240
Denis Vlasenko4b875702009-03-19 13:30:04 +00008241 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008242 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008243 t = trap[sig];
8244 /* non-trapped SIGINT is handled separately by raise_interrupt,
8245 * don't upset it by resetting gotsig[SIGINT-1] */
8246 if (sig == SIGINT && !t)
8247 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008248
8249 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008250 *g = 0;
8251 if (!t)
8252 continue;
8253 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008254 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008255 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008256 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008257 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008258 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008259 }
8260
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008261 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008262 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008263}
8264
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008265/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008266static void evalloop(union node *, int);
8267static void evalfor(union node *, int);
8268static void evalcase(union node *, int);
8269static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008270static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008271static void evalpipe(union node *, int);
8272static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008273static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008274static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008275
Eric Andersen62483552001-07-10 06:09:16 +00008276/*
Eric Andersenc470f442003-07-28 09:56:35 +00008277 * Evaluate a parse tree. The value is left in the global variable
8278 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008279 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008280static void
Eric Andersenc470f442003-07-28 09:56:35 +00008281evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008282{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008283 struct jmploc *volatile savehandler = exception_handler;
8284 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008285 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008286 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008287 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008288 int int_level;
8289
8290 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008291
Eric Andersenc470f442003-07-28 09:56:35 +00008292 if (n == NULL) {
8293 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008294 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008295 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008296 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008297
8298 exception_handler = &jmploc;
8299 {
8300 int err = setjmp(jmploc.loc);
8301 if (err) {
8302 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008303 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008304 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8305 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008306 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008307 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008308 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008309 TRACE(("exception %d in evaltree, propagating err=%d\n",
8310 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008311 exception_handler = savehandler;
8312 longjmp(exception_handler->loc, err);
8313 }
8314 }
8315
Eric Andersenc470f442003-07-28 09:56:35 +00008316 switch (n->type) {
8317 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008318#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008319 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008320 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008321 break;
8322#endif
8323 case NNOT:
8324 evaltree(n->nnot.com, EV_TESTED);
8325 status = !exitstatus;
8326 goto setstatus;
8327 case NREDIR:
8328 expredir(n->nredir.redirect);
8329 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8330 if (!status) {
8331 evaltree(n->nredir.n, flags & EV_TESTED);
8332 status = exitstatus;
8333 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008334 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008335 goto setstatus;
8336 case NCMD:
8337 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008338 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008339 if (eflag && !(flags & EV_TESTED))
8340 checkexit = ~0;
8341 goto calleval;
8342 case NFOR:
8343 evalfn = evalfor;
8344 goto calleval;
8345 case NWHILE:
8346 case NUNTIL:
8347 evalfn = evalloop;
8348 goto calleval;
8349 case NSUBSHELL:
8350 case NBACKGND:
8351 evalfn = evalsubshell;
8352 goto calleval;
8353 case NPIPE:
8354 evalfn = evalpipe;
8355 goto checkexit;
8356 case NCASE:
8357 evalfn = evalcase;
8358 goto calleval;
8359 case NAND:
8360 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008361 case NSEMI: {
8362
Eric Andersenc470f442003-07-28 09:56:35 +00008363#if NAND + 1 != NOR
8364#error NAND + 1 != NOR
8365#endif
8366#if NOR + 1 != NSEMI
8367#error NOR + 1 != NSEMI
8368#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008369 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008370 evaltree(
8371 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008372 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008373 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008374 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008375 break;
8376 if (!evalskip) {
8377 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008378 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008379 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008380 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008381 evalfn(n, flags);
8382 break;
8383 }
8384 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008385 }
Eric Andersenc470f442003-07-28 09:56:35 +00008386 case NIF:
8387 evaltree(n->nif.test, EV_TESTED);
8388 if (evalskip)
8389 break;
8390 if (exitstatus == 0) {
8391 n = n->nif.ifpart;
8392 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008393 }
8394 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008395 n = n->nif.elsepart;
8396 goto evaln;
8397 }
8398 goto success;
8399 case NDEFUN:
8400 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008401 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008402 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008403 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008404 exitstatus = status;
8405 break;
8406 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008407
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008408 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008409 exception_handler = savehandler;
8410 out1:
8411 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008412 evalskip |= SKIPEVAL;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008413 else if (pending_sig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008414 goto exexit;
8415
8416 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008417 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008418 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008419 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008420
8421 RESTORE_INT(int_level);
8422 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008423}
8424
Eric Andersenc470f442003-07-28 09:56:35 +00008425#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8426static
8427#endif
8428void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8429
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008430static void
Eric Andersenc470f442003-07-28 09:56:35 +00008431evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008432{
8433 int status;
8434
8435 loopnest++;
8436 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008437 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008438 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008439 int i;
8440
Eric Andersencb57d552001-06-28 07:25:16 +00008441 evaltree(n->nbinary.ch1, EV_TESTED);
8442 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008443 skipping:
8444 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008445 evalskip = 0;
8446 continue;
8447 }
8448 if (evalskip == SKIPBREAK && --skipcount <= 0)
8449 evalskip = 0;
8450 break;
8451 }
Eric Andersenc470f442003-07-28 09:56:35 +00008452 i = exitstatus;
8453 if (n->type != NWHILE)
8454 i = !i;
8455 if (i != 0)
8456 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008457 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008458 status = exitstatus;
8459 if (evalskip)
8460 goto skipping;
8461 }
8462 loopnest--;
8463 exitstatus = status;
8464}
8465
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008466static void
Eric Andersenc470f442003-07-28 09:56:35 +00008467evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008468{
8469 struct arglist arglist;
8470 union node *argp;
8471 struct strlist *sp;
8472 struct stackmark smark;
8473
8474 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008475 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008476 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008477 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008478 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008479 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008480 if (evalskip)
8481 goto out;
8482 }
8483 *arglist.lastp = NULL;
8484
8485 exitstatus = 0;
8486 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008487 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008488 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008489 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008490 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008491 if (evalskip) {
8492 if (evalskip == SKIPCONT && --skipcount <= 0) {
8493 evalskip = 0;
8494 continue;
8495 }
8496 if (evalskip == SKIPBREAK && --skipcount <= 0)
8497 evalskip = 0;
8498 break;
8499 }
8500 }
8501 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008502 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008503 popstackmark(&smark);
8504}
8505
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008506static void
Eric Andersenc470f442003-07-28 09:56:35 +00008507evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008508{
8509 union node *cp;
8510 union node *patp;
8511 struct arglist arglist;
8512 struct stackmark smark;
8513
8514 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008515 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008516 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008517 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008518 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008519 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8520 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008521 if (casematch(patp, arglist.list->text)) {
8522 if (evalskip == 0) {
8523 evaltree(cp->nclist.body, flags);
8524 }
8525 goto out;
8526 }
8527 }
8528 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008529 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008530 popstackmark(&smark);
8531}
8532
Eric Andersenc470f442003-07-28 09:56:35 +00008533/*
8534 * Kick off a subshell to evaluate a tree.
8535 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008536static void
Eric Andersenc470f442003-07-28 09:56:35 +00008537evalsubshell(union node *n, int flags)
8538{
8539 struct job *jp;
8540 int backgnd = (n->type == NBACKGND);
8541 int status;
8542
8543 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008544 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008545 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008546 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008547 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008548 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008549 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008550 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008551 flags |= EV_EXIT;
8552 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008553 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008554 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008555 redirect(n->nredir.redirect, 0);
8556 evaltreenr(n->nredir.n, flags);
8557 /* never returns */
8558 }
8559 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008560 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008561 status = waitforjob(jp);
8562 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008563 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008564}
8565
Eric Andersenc470f442003-07-28 09:56:35 +00008566/*
8567 * Compute the names of the files in a redirection list.
8568 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008569static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008570static void
8571expredir(union node *n)
8572{
8573 union node *redir;
8574
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008575 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008576 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008577
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008578 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008579 fn.lastp = &fn.list;
8580 switch (redir->type) {
8581 case NFROMTO:
8582 case NFROM:
8583 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008584#if ENABLE_ASH_BASH_COMPAT
8585 case NTO2:
8586#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008587 case NCLOBBER:
8588 case NAPPEND:
8589 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008590#if ENABLE_ASH_BASH_COMPAT
8591 store_expfname:
8592#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008593 redir->nfile.expfname = fn.list->text;
8594 break;
8595 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008596 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008597 if (redir->ndup.vname) {
8598 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008599 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008600 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008601#if ENABLE_ASH_BASH_COMPAT
8602//FIXME: we used expandarg with different args!
8603 if (!isdigit_str9(fn.list->text)) {
8604 /* >&file, not >&fd */
8605 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8606 ash_msg_and_raise_error("redir error");
8607 redir->type = NTO2;
8608 goto store_expfname;
8609 }
8610#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008611 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008612 }
8613 break;
8614 }
8615 }
8616}
8617
Eric Andersencb57d552001-06-28 07:25:16 +00008618/*
Eric Andersencb57d552001-06-28 07:25:16 +00008619 * Evaluate a pipeline. All the processes in the pipeline are children
8620 * of the process creating the pipeline. (This differs from some versions
8621 * of the shell, which make the last process in a pipeline the parent
8622 * of all the rest.)
8623 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008624static void
Eric Andersenc470f442003-07-28 09:56:35 +00008625evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008626{
8627 struct job *jp;
8628 struct nodelist *lp;
8629 int pipelen;
8630 int prevfd;
8631 int pip[2];
8632
Eric Andersenc470f442003-07-28 09:56:35 +00008633 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008634 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008635 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008636 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008637 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008638 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008639 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008640 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008641 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008642 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008643 pip[1] = -1;
8644 if (lp->next) {
8645 if (pipe(pip) < 0) {
8646 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008647 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008648 }
8649 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008650 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008651 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008652 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008653 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008654 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008655 if (prevfd > 0) {
8656 dup2(prevfd, 0);
8657 close(prevfd);
8658 }
8659 if (pip[1] > 1) {
8660 dup2(pip[1], 1);
8661 close(pip[1]);
8662 }
Eric Andersenc470f442003-07-28 09:56:35 +00008663 evaltreenr(lp->n, flags);
8664 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008665 }
8666 if (prevfd >= 0)
8667 close(prevfd);
8668 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008669 /* Don't want to trigger debugging */
8670 if (pip[1] != -1)
8671 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008672 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008673 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008674 exitstatus = waitforjob(jp);
8675 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008676 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008677 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008678}
8679
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008680/*
8681 * Controls whether the shell is interactive or not.
8682 */
8683static void
8684setinteractive(int on)
8685{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008686 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008687
8688 if (++on == is_interactive)
8689 return;
8690 is_interactive = on;
8691 setsignal(SIGINT);
8692 setsignal(SIGQUIT);
8693 setsignal(SIGTERM);
8694#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8695 if (is_interactive > 1) {
8696 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008697 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008698
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008699 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008700 /* note: ash and hush share this string */
8701 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008702 "Enter 'help' for a list of built-in commands."
8703 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008704 bb_banner,
8705 "built-in shell (ash)"
8706 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008707 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008708 }
8709 }
8710#endif
8711}
8712
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008713static void
8714optschanged(void)
8715{
8716#if DEBUG
8717 opentrace();
8718#endif
8719 setinteractive(iflag);
8720 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008721#if ENABLE_FEATURE_EDITING_VI
8722 if (viflag)
8723 line_input_state->flags |= VI_MODE;
8724 else
8725 line_input_state->flags &= ~VI_MODE;
8726#else
8727 viflag = 0; /* forcibly keep the option off */
8728#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008729}
8730
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008731static struct localvar *localvars;
8732
8733/*
8734 * Called after a function returns.
8735 * Interrupts must be off.
8736 */
8737static void
8738poplocalvars(void)
8739{
8740 struct localvar *lvp;
8741 struct var *vp;
8742
8743 while ((lvp = localvars) != NULL) {
8744 localvars = lvp->next;
8745 vp = lvp->vp;
Denys Vlasenko883cea42009-07-11 15:31:59 +02008746 TRACE(("poplocalvar %s\n", vp ? vp->text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008747 if (vp == NULL) { /* $- saved */
8748 memcpy(optlist, lvp->text, sizeof(optlist));
8749 free((char*)lvp->text);
8750 optschanged();
8751 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008752 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008753 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008754 if (vp->var_func)
8755 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008756 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008757 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008758 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008759 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008760 }
8761 free(lvp);
8762 }
8763}
8764
8765static int
8766evalfun(struct funcnode *func, int argc, char **argv, int flags)
8767{
8768 volatile struct shparam saveparam;
8769 struct localvar *volatile savelocalvars;
8770 struct jmploc *volatile savehandler;
8771 struct jmploc jmploc;
8772 int e;
8773
8774 saveparam = shellparam;
8775 savelocalvars = localvars;
8776 e = setjmp(jmploc.loc);
8777 if (e) {
8778 goto funcdone;
8779 }
8780 INT_OFF;
8781 savehandler = exception_handler;
8782 exception_handler = &jmploc;
8783 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008784 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008785 func->count++;
8786 funcnest++;
8787 INT_ON;
8788 shellparam.nparam = argc - 1;
8789 shellparam.p = argv + 1;
8790#if ENABLE_ASH_GETOPTS
8791 shellparam.optind = 1;
8792 shellparam.optoff = -1;
8793#endif
8794 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008795 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008796 INT_OFF;
8797 funcnest--;
8798 freefunc(func);
8799 poplocalvars();
8800 localvars = savelocalvars;
8801 freeparam(&shellparam);
8802 shellparam = saveparam;
8803 exception_handler = savehandler;
8804 INT_ON;
8805 evalskip &= ~SKIPFUNC;
8806 return e;
8807}
8808
Denis Vlasenko131ae172007-02-18 13:00:19 +00008809#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008810static char **
8811parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008812{
8813 char *cp, c;
8814
8815 for (;;) {
8816 cp = *++argv;
8817 if (!cp)
8818 return 0;
8819 if (*cp++ != '-')
8820 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008821 c = *cp++;
8822 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008823 break;
8824 if (c == '-' && !*cp) {
8825 argv++;
8826 break;
8827 }
8828 do {
8829 switch (c) {
8830 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008831 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008832 break;
8833 default:
8834 /* run 'typecmd' for other options */
8835 return 0;
8836 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008837 c = *cp++;
8838 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008839 }
8840 return argv;
8841}
8842#endif
8843
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008844/*
8845 * Make a variable a local variable. When a variable is made local, it's
8846 * value and flags are saved in a localvar structure. The saved values
8847 * will be restored when the shell function returns. We handle the name
8848 * "-" as a special case.
8849 */
8850static void
8851mklocal(char *name)
8852{
8853 struct localvar *lvp;
8854 struct var **vpp;
8855 struct var *vp;
8856
8857 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008858 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008859 if (LONE_DASH(name)) {
8860 char *p;
8861 p = ckmalloc(sizeof(optlist));
8862 lvp->text = memcpy(p, optlist, sizeof(optlist));
8863 vp = NULL;
8864 } else {
8865 char *eq;
8866
8867 vpp = hashvar(name);
8868 vp = *findvar(vpp, name);
8869 eq = strchr(name, '=');
8870 if (vp == NULL) {
8871 if (eq)
8872 setvareq(name, VSTRFIXED);
8873 else
8874 setvar(name, NULL, VSTRFIXED);
8875 vp = *vpp; /* the new variable */
8876 lvp->flags = VUNSET;
8877 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008878 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008879 lvp->flags = vp->flags;
8880 vp->flags |= VSTRFIXED|VTEXTFIXED;
8881 if (eq)
8882 setvareq(name, 0);
8883 }
8884 }
8885 lvp->vp = vp;
8886 lvp->next = localvars;
8887 localvars = lvp;
8888 INT_ON;
8889}
8890
8891/*
8892 * The "local" command.
8893 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008894static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008895localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008896{
8897 char *name;
8898
8899 argv = argptr;
8900 while ((name = *argv++) != NULL) {
8901 mklocal(name);
8902 }
8903 return 0;
8904}
8905
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008906static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008907falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008908{
8909 return 1;
8910}
8911
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008912static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008913truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008914{
8915 return 0;
8916}
8917
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008918static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008919execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008920{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008921 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008922 iflag = 0; /* exit on error */
8923 mflag = 0;
8924 optschanged();
8925 shellexec(argv + 1, pathval(), 0);
8926 }
8927 return 0;
8928}
8929
8930/*
8931 * The return command.
8932 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008933static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008934returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008935{
8936 /*
8937 * If called outside a function, do what ksh does;
8938 * skip the rest of the file.
8939 */
8940 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8941 return argv[1] ? number(argv[1]) : exitstatus;
8942}
8943
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008944/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008945static int breakcmd(int, char **) FAST_FUNC;
8946static int dotcmd(int, char **) FAST_FUNC;
8947static int evalcmd(int, char **) FAST_FUNC;
8948static int exitcmd(int, char **) FAST_FUNC;
8949static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008950#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008951static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008952#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008953#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008954static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00008955#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00008956#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008957static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008958#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008959static int readcmd(int, char **) FAST_FUNC;
8960static int setcmd(int, char **) FAST_FUNC;
8961static int shiftcmd(int, char **) FAST_FUNC;
8962static int timescmd(int, char **) FAST_FUNC;
8963static int trapcmd(int, char **) FAST_FUNC;
8964static int umaskcmd(int, char **) FAST_FUNC;
8965static int unsetcmd(int, char **) FAST_FUNC;
8966static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008967
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008968#define BUILTIN_NOSPEC "0"
8969#define BUILTIN_SPECIAL "1"
8970#define BUILTIN_REGULAR "2"
8971#define BUILTIN_SPEC_REG "3"
8972#define BUILTIN_ASSIGN "4"
8973#define BUILTIN_SPEC_ASSG "5"
8974#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008975#define BUILTIN_SPEC_REG_ASSG "7"
8976
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008977/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008978#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008979static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008980#endif
8981#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008982static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008983#endif
8984#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008985static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008986#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00008987
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008988/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008989static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01008990 { BUILTIN_SPEC_REG "." , dotcmd },
8991 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008992#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01008993 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008994#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01008995 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008996#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008997#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008998#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01008999 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009000#endif
9001#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009002 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009003#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009004 { BUILTIN_SPEC_REG "break" , breakcmd },
9005 { BUILTIN_REGULAR "cd" , cdcmd },
9006 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009007#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009008 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009009#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009010 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009011#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009012 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009013#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009014 { BUILTIN_SPEC_REG "eval" , evalcmd },
9015 { BUILTIN_SPEC_REG "exec" , execcmd },
9016 { BUILTIN_SPEC_REG "exit" , exitcmd },
9017 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9018 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009019#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009020 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009021#endif
9022#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009023 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009024#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009025 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009026#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009027 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009028#endif
9029#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009030 { BUILTIN_REGULAR "jobs" , jobscmd },
9031 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009032#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009033#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009034 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009035#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009036 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009037#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009038 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009039#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009040 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9041 { BUILTIN_REGULAR "read" , readcmd },
9042 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9043 { BUILTIN_SPEC_REG "return" , returncmd },
9044 { BUILTIN_SPEC_REG "set" , setcmd },
9045 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009046#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009047 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009048#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009049#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009050 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009051#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009052 { BUILTIN_SPEC_REG "times" , timescmd },
9053 { BUILTIN_SPEC_REG "trap" , trapcmd },
9054 { BUILTIN_REGULAR "true" , truecmd },
9055 { BUILTIN_NOSPEC "type" , typecmd },
9056 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9057 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009058#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009059 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009060#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009061 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9062 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009063};
9064
Denis Vlasenko80591b02008-03-25 07:49:43 +00009065/* Should match the above table! */
9066#define COMMANDCMD (builtintab + \
9067 2 + \
9068 1 * ENABLE_ASH_BUILTIN_TEST + \
9069 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9070 1 * ENABLE_ASH_ALIAS + \
9071 1 * ENABLE_ASH_JOB_CONTROL + \
9072 3)
9073#define EXECCMD (builtintab + \
9074 2 + \
9075 1 * ENABLE_ASH_BUILTIN_TEST + \
9076 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9077 1 * ENABLE_ASH_ALIAS + \
9078 1 * ENABLE_ASH_JOB_CONTROL + \
9079 3 + \
9080 1 * ENABLE_ASH_CMDCMD + \
9081 1 + \
9082 ENABLE_ASH_BUILTIN_ECHO + \
9083 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009084
9085/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009086 * Search the table of builtin commands.
9087 */
9088static struct builtincmd *
9089find_builtin(const char *name)
9090{
9091 struct builtincmd *bp;
9092
9093 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009094 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009095 pstrcmp
9096 );
9097 return bp;
9098}
9099
9100/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009101 * Execute a simple command.
9102 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009103static int
9104isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009105{
9106 const char *q = endofname(p);
9107 if (p == q)
9108 return 0;
9109 return *q == '=';
9110}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009111static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009112bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009113{
9114 /* Preserve exitstatus of a previous possible redirection
9115 * as POSIX mandates */
9116 return back_exitstatus;
9117}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009118static void
Eric Andersenc470f442003-07-28 09:56:35 +00009119evalcommand(union node *cmd, int flags)
9120{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009121 static const struct builtincmd null_bltin = {
9122 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009123 };
Eric Andersenc470f442003-07-28 09:56:35 +00009124 struct stackmark smark;
9125 union node *argp;
9126 struct arglist arglist;
9127 struct arglist varlist;
9128 char **argv;
9129 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009130 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009131 struct cmdentry cmdentry;
9132 struct job *jp;
9133 char *lastarg;
9134 const char *path;
9135 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009136 int status;
9137 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009138 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009139 smallint cmd_is_exec;
9140 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009141
9142 /* First expand the arguments. */
9143 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9144 setstackmark(&smark);
9145 back_exitstatus = 0;
9146
9147 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009148 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009149 varlist.lastp = &varlist.list;
9150 *varlist.lastp = NULL;
9151 arglist.lastp = &arglist.list;
9152 *arglist.lastp = NULL;
9153
9154 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009155 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009156 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9157 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9158 }
9159
Eric Andersenc470f442003-07-28 09:56:35 +00009160 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9161 struct strlist **spp;
9162
9163 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009164 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009165 expandarg(argp, &arglist, EXP_VARTILDE);
9166 else
9167 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9168
Eric Andersenc470f442003-07-28 09:56:35 +00009169 for (sp = *spp; sp; sp = sp->next)
9170 argc++;
9171 }
9172
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009173 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009174 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009175 TRACE(("evalcommand arg: %s\n", sp->text));
9176 *nargv++ = sp->text;
9177 }
9178 *nargv = NULL;
9179
9180 lastarg = NULL;
9181 if (iflag && funcnest == 0 && argc > 0)
9182 lastarg = nargv[-1];
9183
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009184 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009185 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009186 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009187
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009188 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009189 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9190 struct strlist **spp;
9191 char *p;
9192
9193 spp = varlist.lastp;
9194 expandarg(argp, &varlist, EXP_VARTILDE);
9195
9196 /*
9197 * Modify the command lookup path, if a PATH= assignment
9198 * is present
9199 */
9200 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009201 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009202 path = p;
9203 }
9204
9205 /* Print the command if xflag is set. */
9206 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009207 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009208 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009209
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009210 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009211 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009212 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009213 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009214 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009215 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009216 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009217 }
9218 sp = arglist.list;
9219 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009220 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009221 }
9222
9223 cmd_is_exec = 0;
9224 spclbltin = -1;
9225
9226 /* Now locate the command. */
9227 if (argc) {
9228 const char *oldpath;
9229 int cmd_flag = DO_ERR;
9230
9231 path += 5;
9232 oldpath = path;
9233 for (;;) {
9234 find_command(argv[0], &cmdentry, cmd_flag, path);
9235 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009236 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009237 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009238 goto bail;
9239 }
9240
9241 /* implement bltin and command here */
9242 if (cmdentry.cmdtype != CMDBUILTIN)
9243 break;
9244 if (spclbltin < 0)
9245 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9246 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009247 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009248#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009249 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009250 path = oldpath;
9251 nargv = parse_command_args(argv, &path);
9252 if (!nargv)
9253 break;
9254 argc -= nargv - argv;
9255 argv = nargv;
9256 cmd_flag |= DO_NOFUNC;
9257 } else
9258#endif
9259 break;
9260 }
9261 }
9262
9263 if (status) {
9264 /* We have a redirection error. */
9265 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009266 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009267 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009268 exitstatus = status;
9269 goto out;
9270 }
9271
9272 /* Execute the command. */
9273 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009274 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009275
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009276#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009277/* (1) BUG: if variables are set, we need to fork, or save/restore them
9278 * around run_nofork_applet() call.
9279 * (2) Should this check also be done in forkshell()?
9280 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9281 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009282 /* find_command() encodes applet_no as (-2 - applet_no) */
9283 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009284 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009285 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009286 /* run <applet>_main() */
9287 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009288 break;
9289 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009290#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009291 /* Can we avoid forking off? For example, very last command
9292 * in a script or a subshell does not need forking,
9293 * we can just exec it.
9294 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009295 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009296 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009297 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009298 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009299 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009300 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009301 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009302 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009303 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009304 break;
9305 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009306 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009307 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009308 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009309 }
9310 listsetvar(varlist.list, VEXPORT|VSTACK);
9311 shellexec(argv, path, cmdentry.u.index);
9312 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009313 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009314 case CMDBUILTIN:
9315 cmdenviron = varlist.list;
9316 if (cmdenviron) {
9317 struct strlist *list = cmdenviron;
9318 int i = VNOSET;
9319 if (spclbltin > 0 || argc == 0) {
9320 i = 0;
9321 if (cmd_is_exec && argc > 1)
9322 i = VEXPORT;
9323 }
9324 listsetvar(list, i);
9325 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009326 /* Tight loop with builtins only:
9327 * "while kill -0 $child; do true; done"
9328 * will never exit even if $child died, unless we do this
9329 * to reap the zombie and make kill detect that it's gone: */
9330 dowait(DOWAIT_NONBLOCK, NULL);
9331
Eric Andersenc470f442003-07-28 09:56:35 +00009332 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9333 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009334 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009335 if (i == EXEXIT)
9336 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009337 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009338 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009339 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009340 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009341 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009342 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009343 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009344 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009345 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009346 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009347 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009348 }
9349 break;
9350
9351 case CMDFUNCTION:
9352 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009353 /* See above for the rationale */
9354 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009355 if (evalfun(cmdentry.u.func, argc, argv, flags))
9356 goto raise;
9357 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009358
9359 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009360
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009361 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009362 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009363 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009364 /* dsl: I think this is intended to be used to support
9365 * '_' in 'vi' command mode during line editing...
9366 * However I implemented that within libedit itself.
9367 */
9368 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009369 }
Eric Andersenc470f442003-07-28 09:56:35 +00009370 popstackmark(&smark);
9371}
9372
9373static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009374evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9375{
Eric Andersenc470f442003-07-28 09:56:35 +00009376 char *volatile savecmdname;
9377 struct jmploc *volatile savehandler;
9378 struct jmploc jmploc;
9379 int i;
9380
9381 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009382 i = setjmp(jmploc.loc);
9383 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009384 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009385 savehandler = exception_handler;
9386 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009387 commandname = argv[0];
9388 argptr = argv + 1;
9389 optptr = NULL; /* initialize nextopt */
9390 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009391 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009392 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009393 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009394 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009395 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009396 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009397
9398 return i;
9399}
9400
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009401static int
9402goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009403{
9404 return !*endofname(p);
9405}
9406
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009407
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009408/*
9409 * Search for a command. This is called before we fork so that the
9410 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009411 * the child. The check for "goodname" is an overly conservative
9412 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009413 */
Eric Andersenc470f442003-07-28 09:56:35 +00009414static void
9415prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009416{
9417 struct cmdentry entry;
9418
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009419 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9420 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009421}
9422
Eric Andersencb57d552001-06-28 07:25:16 +00009423
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009424/* ============ Builtin commands
9425 *
9426 * Builtin commands whose functions are closely tied to evaluation
9427 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009428 */
9429
9430/*
Eric Andersencb57d552001-06-28 07:25:16 +00009431 * Handle break and continue commands. Break, continue, and return are
9432 * all handled by setting the evalskip flag. The evaluation routines
9433 * above all check this flag, and if it is set they start skipping
9434 * commands rather than executing them. The variable skipcount is
9435 * the number of loops to break/continue, or the number of function
9436 * levels to return. (The latter is always 1.) It should probably
9437 * be an error to break out of more loops than exist, but it isn't
9438 * in the standard shell so we don't make it one here.
9439 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009440static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009441breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009442{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009443 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009444
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009445 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009446 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009447 if (n > loopnest)
9448 n = loopnest;
9449 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009450 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009451 skipcount = n;
9452 }
9453 return 0;
9454}
9455
Eric Andersenc470f442003-07-28 09:56:35 +00009456
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009457/* ============ input.c
9458 *
Eric Andersen90898442003-08-06 11:20:52 +00009459 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009460 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009461
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009462enum {
9463 INPUT_PUSH_FILE = 1,
9464 INPUT_NOFILE_OK = 2,
9465};
Eric Andersencb57d552001-06-28 07:25:16 +00009466
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009467static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009468/* values of checkkwd variable */
9469#define CHKALIAS 0x1
9470#define CHKKWD 0x2
9471#define CHKNL 0x4
9472
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009473/*
9474 * Push a string back onto the input at this current parsefile level.
9475 * We handle aliases this way.
9476 */
9477#if !ENABLE_ASH_ALIAS
9478#define pushstring(s, ap) pushstring(s)
9479#endif
9480static void
9481pushstring(char *s, struct alias *ap)
9482{
9483 struct strpush *sp;
9484 int len;
9485
9486 len = strlen(s);
9487 INT_OFF;
9488 if (g_parsefile->strpush) {
9489 sp = ckzalloc(sizeof(*sp));
9490 sp->prev = g_parsefile->strpush;
9491 } else {
9492 sp = &(g_parsefile->basestrpush);
9493 }
9494 g_parsefile->strpush = sp;
9495 sp->prev_string = g_parsefile->next_to_pgetc;
9496 sp->prev_left_in_line = g_parsefile->left_in_line;
9497#if ENABLE_ASH_ALIAS
9498 sp->ap = ap;
9499 if (ap) {
9500 ap->flag |= ALIASINUSE;
9501 sp->string = s;
9502 }
9503#endif
9504 g_parsefile->next_to_pgetc = s;
9505 g_parsefile->left_in_line = len;
9506 INT_ON;
9507}
9508
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009509static void
9510popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009511{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009512 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009513
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009514 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009515#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009516 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009517 if (g_parsefile->next_to_pgetc[-1] == ' '
9518 || g_parsefile->next_to_pgetc[-1] == '\t'
9519 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009520 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009521 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009522 if (sp->string != sp->ap->val) {
9523 free(sp->string);
9524 }
9525 sp->ap->flag &= ~ALIASINUSE;
9526 if (sp->ap->flag & ALIASDEAD) {
9527 unalias(sp->ap->name);
9528 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009529 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009530#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009531 g_parsefile->next_to_pgetc = sp->prev_string;
9532 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009533 g_parsefile->strpush = sp->prev;
9534 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009535 free(sp);
9536 INT_ON;
9537}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009538
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009539//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9540//it peeks whether it is &>, and then pushes back both chars.
9541//This function needs to save last *next_to_pgetc to buf[0]
9542//to make two pungetc() reliable. Currently,
9543// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009544static int
9545preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009546{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009547 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009548 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009549
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009550 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009551#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009552 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009553 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9554 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009555 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009556#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009557 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009558#endif
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02009559 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009560 if (nr == 0) {
9561 /* Ctrl+C pressed */
9562 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009563 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009564 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009565 raise(SIGINT);
9566 return 1;
9567 }
Eric Andersenc470f442003-07-28 09:56:35 +00009568 goto retry;
9569 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009570 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009571 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009572 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009573 }
Eric Andersencb57d552001-06-28 07:25:16 +00009574 }
9575#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009576 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009577#endif
9578
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009579#if 0
9580/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009581 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009582 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009583 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009584 if (flags >= 0 && (flags & O_NONBLOCK)) {
9585 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009586 if (fcntl(0, F_SETFL, flags) >= 0) {
9587 out2str("sh: turning off NDELAY mode\n");
9588 goto retry;
9589 }
9590 }
9591 }
9592 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009593#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009594 return nr;
9595}
9596
9597/*
9598 * Refill the input buffer and return the next input character:
9599 *
9600 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009601 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9602 * or we are reading from a string so we can't refill the buffer,
9603 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009604 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009605 * 4) Process input up to the next newline, deleting nul characters.
9606 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009607//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9608#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009609static int
Eric Andersenc470f442003-07-28 09:56:35 +00009610preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009611{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009612 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009613 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009614
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009615 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009616#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009617 if (g_parsefile->left_in_line == -1
9618 && g_parsefile->strpush->ap
9619 && g_parsefile->next_to_pgetc[-1] != ' '
9620 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009621 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009622 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009623 return PEOA;
9624 }
Eric Andersen2870d962001-07-02 17:27:21 +00009625#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009626 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009627 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009628 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9629 g_parsefile->left_in_line,
9630 g_parsefile->next_to_pgetc,
9631 g_parsefile->next_to_pgetc);
9632 if (--g_parsefile->left_in_line >= 0)
9633 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009634 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009635 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009636 * "pgetc" needs refilling.
9637 */
9638
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009639 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009640 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009641 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009642 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009643 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009644 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009645 /* even in failure keep left_in_line and next_to_pgetc
9646 * in lock step, for correct multi-layer pungetc.
9647 * left_in_line was decremented before preadbuffer(),
9648 * must inc next_to_pgetc: */
9649 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009650 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009651 }
Eric Andersencb57d552001-06-28 07:25:16 +00009652
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009653 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009654 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009655 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009656 again:
9657 more = preadfd();
9658 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009659 /* don't try reading again */
9660 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009661 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009662 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009663 return PEOF;
9664 }
9665 }
9666
Denis Vlasenko727752d2008-11-28 03:41:47 +00009667 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009668 * Set g_parsefile->left_in_line
9669 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009670 * NUL chars are deleted.
9671 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009672 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009673 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009674 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009675
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009676 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009677
Denis Vlasenko727752d2008-11-28 03:41:47 +00009678 c = *q;
9679 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009680 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009681 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009682 q++;
9683 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009684 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009685 break;
9686 }
Eric Andersencb57d552001-06-28 07:25:16 +00009687 }
9688
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009689 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009690 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9691 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009692 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009693 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009694 }
9695 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009696 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009697
Eric Andersencb57d552001-06-28 07:25:16 +00009698 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009699 char save = *q;
9700 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009701 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009702 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009703 }
9704
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009705 pgetc_debug("preadbuffer at %d:%p'%s'",
9706 g_parsefile->left_in_line,
9707 g_parsefile->next_to_pgetc,
9708 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009709 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009710}
9711
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009712#define pgetc_as_macro() \
9713 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009714 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009715 : preadbuffer() \
9716 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009717
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009718static int
9719pgetc(void)
9720{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009721 pgetc_debug("pgetc_fast at %d:%p'%s'",
9722 g_parsefile->left_in_line,
9723 g_parsefile->next_to_pgetc,
9724 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009725 return pgetc_as_macro();
9726}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009727
9728#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009729# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009730#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009731# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009732#endif
9733
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009734#if ENABLE_ASH_ALIAS
9735static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009736pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009737{
9738 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009739 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009740 pgetc_debug("pgetc_fast at %d:%p'%s'",
9741 g_parsefile->left_in_line,
9742 g_parsefile->next_to_pgetc,
9743 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009744 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009745 } while (c == PEOA);
9746 return c;
9747}
9748#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009749# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009750#endif
9751
9752/*
9753 * Read a line from the script.
9754 */
9755static char *
9756pfgets(char *line, int len)
9757{
9758 char *p = line;
9759 int nleft = len;
9760 int c;
9761
9762 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009763 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009764 if (c == PEOF) {
9765 if (p == line)
9766 return NULL;
9767 break;
9768 }
9769 *p++ = c;
9770 if (c == '\n')
9771 break;
9772 }
9773 *p = '\0';
9774 return line;
9775}
9776
Eric Andersenc470f442003-07-28 09:56:35 +00009777/*
9778 * Undo the last call to pgetc. Only one character may be pushed back.
9779 * PEOF may be pushed back.
9780 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009781static void
Eric Andersenc470f442003-07-28 09:56:35 +00009782pungetc(void)
9783{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009784 g_parsefile->left_in_line++;
9785 g_parsefile->next_to_pgetc--;
9786 pgetc_debug("pushed back to %d:%p'%s'",
9787 g_parsefile->left_in_line,
9788 g_parsefile->next_to_pgetc,
9789 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009790}
9791
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009792/*
9793 * To handle the "." command, a stack of input files is used. Pushfile
9794 * adds a new entry to the stack and popfile restores the previous level.
9795 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009796static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009797pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009798{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009799 struct parsefile *pf;
9800
Denis Vlasenko597906c2008-02-20 16:38:54 +00009801 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009802 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009803 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009804 /*pf->strpush = NULL; - ckzalloc did it */
9805 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009806 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009807}
9808
9809static void
9810popfile(void)
9811{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009812 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009813
Denis Vlasenkob012b102007-02-19 22:43:01 +00009814 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009815 if (pf->pf_fd >= 0)
9816 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009817 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009818 while (pf->strpush)
9819 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009820 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009821 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009822 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009823}
9824
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009825/*
9826 * Return to top level.
9827 */
9828static void
9829popallfiles(void)
9830{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009831 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009832 popfile();
9833}
9834
9835/*
9836 * Close the file(s) that the shell is reading commands from. Called
9837 * after a fork is done.
9838 */
9839static void
9840closescript(void)
9841{
9842 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009843 if (g_parsefile->pf_fd > 0) {
9844 close(g_parsefile->pf_fd);
9845 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009846 }
9847}
9848
9849/*
9850 * Like setinputfile, but takes an open file descriptor. Call this with
9851 * interrupts off.
9852 */
9853static void
9854setinputfd(int fd, int push)
9855{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009856 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009857 if (push) {
9858 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009859 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009860 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009861 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009862 if (g_parsefile->buf == NULL)
9863 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009864 g_parsefile->left_in_buffer = 0;
9865 g_parsefile->left_in_line = 0;
9866 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009867}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009868
Eric Andersenc470f442003-07-28 09:56:35 +00009869/*
9870 * Set the input to take input from a file. If push is set, push the
9871 * old input onto the stack first.
9872 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009873static int
9874setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009875{
9876 int fd;
9877 int fd2;
9878
Denis Vlasenkob012b102007-02-19 22:43:01 +00009879 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009880 fd = open(fname, O_RDONLY);
9881 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009882 if (flags & INPUT_NOFILE_OK)
9883 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009884 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009885 }
Eric Andersenc470f442003-07-28 09:56:35 +00009886 if (fd < 10) {
9887 fd2 = copyfd(fd, 10);
9888 close(fd);
9889 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009890 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009891 fd = fd2;
9892 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009893 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009894 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009895 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009896 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009897}
9898
Eric Andersencb57d552001-06-28 07:25:16 +00009899/*
9900 * Like setinputfile, but takes input from a string.
9901 */
Eric Andersenc470f442003-07-28 09:56:35 +00009902static void
9903setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009904{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009905 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009906 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009907 g_parsefile->next_to_pgetc = string;
9908 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009909 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009910 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009911 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009912}
9913
9914
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009915/* ============ mail.c
9916 *
9917 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009918 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009919
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009920#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009921
Eric Andersencb57d552001-06-28 07:25:16 +00009922#define MAXMBOXES 10
9923
Eric Andersenc470f442003-07-28 09:56:35 +00009924/* times of mailboxes */
9925static time_t mailtime[MAXMBOXES];
9926/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009927static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009928
Eric Andersencb57d552001-06-28 07:25:16 +00009929/*
Eric Andersenc470f442003-07-28 09:56:35 +00009930 * Print appropriate message(s) if mail has arrived.
9931 * If mail_var_path_changed is set,
9932 * then the value of MAIL has mail_var_path_changed,
9933 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009934 */
Eric Andersenc470f442003-07-28 09:56:35 +00009935static void
9936chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009937{
Eric Andersencb57d552001-06-28 07:25:16 +00009938 const char *mpath;
9939 char *p;
9940 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009941 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009942 struct stackmark smark;
9943 struct stat statb;
9944
Eric Andersencb57d552001-06-28 07:25:16 +00009945 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009946 mpath = mpathset() ? mpathval() : mailval();
9947 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02009948 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00009949 if (p == NULL)
9950 break;
9951 if (*p == '\0')
9952 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009953 for (q = p; *q; q++)
9954 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009955#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009956 if (q[-1] != '/')
9957 abort();
9958#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009959 q[-1] = '\0'; /* delete trailing '/' */
9960 if (stat(p, &statb) < 0) {
9961 *mtp = 0;
9962 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009963 }
Eric Andersenc470f442003-07-28 09:56:35 +00009964 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9965 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02009966 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +00009967 pathopt ? pathopt : "you have mail"
9968 );
9969 }
9970 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009971 }
Eric Andersenc470f442003-07-28 09:56:35 +00009972 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009973 popstackmark(&smark);
9974}
Eric Andersencb57d552001-06-28 07:25:16 +00009975
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009976static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009977changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009978{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009979 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009980}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009981
Denis Vlasenko131ae172007-02-18 13:00:19 +00009982#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009983
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009984
9985/* ============ ??? */
9986
Eric Andersencb57d552001-06-28 07:25:16 +00009987/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009988 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009989 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009990static void
9991setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009992{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009993 char **newparam;
9994 char **ap;
9995 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009996
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009997 for (nparam = 0; argv[nparam]; nparam++)
9998 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009999 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10000 while (*argv) {
10001 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010002 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010003 *ap = NULL;
10004 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010005 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010006 shellparam.nparam = nparam;
10007 shellparam.p = newparam;
10008#if ENABLE_ASH_GETOPTS
10009 shellparam.optind = 1;
10010 shellparam.optoff = -1;
10011#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010012}
10013
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010014/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010015 * Process shell options. The global variable argptr contains a pointer
10016 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010017 *
10018 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10019 * For a non-interactive shell, an error condition encountered
10020 * by a special built-in ... shall cause the shell to write a diagnostic message
10021 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010022 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010023 * ...
10024 * Utility syntax error (option or operand error) Shall exit
10025 * ...
10026 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10027 * we see that bash does not do that (set "finishes" with error code 1 instead,
10028 * and shell continues), and people rely on this behavior!
10029 * Testcase:
10030 * set -o barfoo 2>/dev/null
10031 * echo $?
10032 *
10033 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010034 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010035static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010036plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010037{
10038 int i;
10039
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010040 if (name) {
10041 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010042 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010043 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010044 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010045 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010046 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010047 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010048 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010049 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010050 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010051 if (val) {
10052 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10053 } else {
10054 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10055 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010056 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010057 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010058}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010059static void
10060setoption(int flag, int val)
10061{
10062 int i;
10063
10064 for (i = 0; i < NOPTS; i++) {
10065 if (optletters(i) == flag) {
10066 optlist[i] = val;
10067 return;
10068 }
10069 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010070 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010071 /* NOTREACHED */
10072}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010073static int
Eric Andersenc470f442003-07-28 09:56:35 +000010074options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010075{
10076 char *p;
10077 int val;
10078 int c;
10079
10080 if (cmdline)
10081 minusc = NULL;
10082 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010083 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010084 if (c != '-' && c != '+')
10085 break;
10086 argptr++;
10087 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010088 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010089 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010090 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010091 if (!cmdline) {
10092 /* "-" means turn off -x and -v */
10093 if (p[0] == '\0')
10094 xflag = vflag = 0;
10095 /* "--" means reset params */
10096 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010097 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010098 }
Eric Andersenc470f442003-07-28 09:56:35 +000010099 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010100 }
Eric Andersencb57d552001-06-28 07:25:16 +000010101 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010102 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010103 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010104 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010105 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010106 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010107 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010108 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010109 /* it already printed err message */
10110 return 1; /* error */
10111 }
Eric Andersencb57d552001-06-28 07:25:16 +000010112 if (*argptr)
10113 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010114 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10115 isloginsh = 1;
10116 /* bash does not accept +-login, we also won't */
10117 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010118 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010119 isloginsh = 1;
10120 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010121 } else {
10122 setoption(c, val);
10123 }
10124 }
10125 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010126 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010127}
10128
Eric Andersencb57d552001-06-28 07:25:16 +000010129/*
Eric Andersencb57d552001-06-28 07:25:16 +000010130 * The shift builtin command.
10131 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010132static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010133shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010134{
10135 int n;
10136 char **ap1, **ap2;
10137
10138 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010139 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010140 n = number(argv[1]);
10141 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010142 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010143 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010144 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010145 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010146 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010147 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010148 }
10149 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010150 while ((*ap2++ = *ap1++) != NULL)
10151 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010152#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010153 shellparam.optind = 1;
10154 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010155#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010156 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010157 return 0;
10158}
10159
Eric Andersencb57d552001-06-28 07:25:16 +000010160/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010161 * POSIX requires that 'set' (but not export or readonly) output the
10162 * variables in lexicographic order - by the locale's collating order (sigh).
10163 * Maybe we could keep them in an ordered balanced binary tree
10164 * instead of hashed lists.
10165 * For now just roll 'em through qsort for printing...
10166 */
10167static int
10168showvars(const char *sep_prefix, int on, int off)
10169{
10170 const char *sep;
10171 char **ep, **epend;
10172
10173 ep = listvars(on, off, &epend);
10174 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10175
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010176 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010177
10178 for (; ep < epend; ep++) {
10179 const char *p;
10180 const char *q;
10181
10182 p = strchrnul(*ep, '=');
10183 q = nullstr;
10184 if (*p)
10185 q = single_quote(++p);
10186 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10187 }
10188 return 0;
10189}
10190
10191/*
Eric Andersencb57d552001-06-28 07:25:16 +000010192 * The set command builtin.
10193 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010194static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010195setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010196{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010197 int retval;
10198
Denis Vlasenko68404f12008-03-17 09:00:54 +000010199 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010200 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010201 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010202 retval = 1;
10203 if (!options(0)) { /* if no parse error... */
10204 retval = 0;
10205 optschanged();
10206 if (*argptr != NULL) {
10207 setparam(argptr);
10208 }
Eric Andersencb57d552001-06-28 07:25:16 +000010209 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010210 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010211 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010212}
10213
Denis Vlasenko131ae172007-02-18 13:00:19 +000010214#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010215static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010216change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010217{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010218 uint32_t t;
10219
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010220 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010221 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010222 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010223 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010224 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010225 vrandom.flags &= ~VNOFUNC;
10226 } else {
10227 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010228 t = strtoul(value, NULL, 10);
10229 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010230 }
Eric Andersenef02f822004-03-11 13:34:24 +000010231}
Eric Andersen16767e22004-03-16 05:14:10 +000010232#endif
10233
Denis Vlasenko131ae172007-02-18 13:00:19 +000010234#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010235static int
Eric Andersenc470f442003-07-28 09:56:35 +000010236getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010237{
10238 char *p, *q;
10239 char c = '?';
10240 int done = 0;
10241 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010242 char s[12];
10243 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010244
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010245 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010246 return 1;
10247 optnext = optfirst + *param_optind - 1;
10248
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010249 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010250 p = NULL;
10251 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010252 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010253 if (p == NULL || *p == '\0') {
10254 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010255 p = *optnext;
10256 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010257 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010258 p = NULL;
10259 done = 1;
10260 goto out;
10261 }
10262 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010263 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010264 goto atend;
10265 }
10266
10267 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010268 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010269 if (*q == '\0') {
10270 if (optstr[0] == ':') {
10271 s[0] = c;
10272 s[1] = '\0';
10273 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010274 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010275 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010276 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010277 }
10278 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010279 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010280 }
10281 if (*++q == ':')
10282 q++;
10283 }
10284
10285 if (*++q == ':') {
10286 if (*p == '\0' && (p = *optnext) == NULL) {
10287 if (optstr[0] == ':') {
10288 s[0] = c;
10289 s[1] = '\0';
10290 err |= setvarsafe("OPTARG", s, 0);
10291 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010292 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010293 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010294 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010295 c = '?';
10296 }
Eric Andersenc470f442003-07-28 09:56:35 +000010297 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010298 }
10299
10300 if (p == *optnext)
10301 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010302 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010303 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010304 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010305 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010306 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010307 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010308 *param_optind = optnext - optfirst + 1;
10309 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010310 err |= setvarsafe("OPTIND", s, VNOFUNC);
10311 s[0] = c;
10312 s[1] = '\0';
10313 err |= setvarsafe(optvar, s, 0);
10314 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010315 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010316 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010317 flush_stdout_stderr();
10318 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010319 }
10320 return done;
10321}
Eric Andersenc470f442003-07-28 09:56:35 +000010322
10323/*
10324 * The getopts builtin. Shellparam.optnext points to the next argument
10325 * to be processed. Shellparam.optptr points to the next character to
10326 * be processed in the current argument. If shellparam.optnext is NULL,
10327 * then it's the first time getopts has been called.
10328 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010329static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010330getoptscmd(int argc, char **argv)
10331{
10332 char **optbase;
10333
10334 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010335 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010336 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010337 optbase = shellparam.p;
10338 if (shellparam.optind > shellparam.nparam + 1) {
10339 shellparam.optind = 1;
10340 shellparam.optoff = -1;
10341 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010342 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010343 optbase = &argv[3];
10344 if (shellparam.optind > argc - 2) {
10345 shellparam.optind = 1;
10346 shellparam.optoff = -1;
10347 }
10348 }
10349
10350 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010351 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010352}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010353#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010354
Eric Andersencb57d552001-06-28 07:25:16 +000010355
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010356/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010357
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010358struct heredoc {
10359 struct heredoc *next; /* next here document in list */
10360 union node *here; /* redirection node */
10361 char *eofmark; /* string indicating end of input */
10362 smallint striptabs; /* if set, strip leading tabs */
10363};
10364
10365static smallint tokpushback; /* last token pushed back */
10366static smallint parsebackquote; /* nonzero if we are inside backquotes */
10367static smallint quoteflag; /* set if (part of) last token was quoted */
10368static token_id_t lasttoken; /* last token read (integer id Txxx) */
10369static struct heredoc *heredoclist; /* list of here documents to read */
10370static char *wordtext; /* text of last word returned by readtoken */
10371static struct nodelist *backquotelist;
10372static union node *redirnode;
10373static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010374
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010375static const char *
10376tokname(char *buf, int tok)
10377{
10378 if (tok < TSEMI)
10379 return tokname_array[tok] + 1;
10380 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10381 return buf;
10382}
10383
10384/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010385 * Called when an unexpected token is read during the parse. The argument
10386 * is the token that is expected, or -1 if more than one type of token can
10387 * occur at this point.
10388 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010389static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010390static void
10391raise_error_unexpected_syntax(int token)
10392{
10393 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010394 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010395 int l;
10396
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010397 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010398 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010399 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010400 raise_error_syntax(msg);
10401 /* NOTREACHED */
10402}
Eric Andersencb57d552001-06-28 07:25:16 +000010403
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010404#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010405
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010406/* parsing is heavily cross-recursive, need these forward decls */
10407static union node *andor(void);
10408static union node *pipeline(void);
10409static union node *parse_command(void);
10410static void parseheredoc(void);
10411static char peektoken(void);
10412static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010413
Eric Andersenc470f442003-07-28 09:56:35 +000010414static union node *
10415list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010416{
10417 union node *n1, *n2, *n3;
10418 int tok;
10419
Eric Andersenc470f442003-07-28 09:56:35 +000010420 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10421 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010422 return NULL;
10423 n1 = NULL;
10424 for (;;) {
10425 n2 = andor();
10426 tok = readtoken();
10427 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010428 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010429 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010430 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010431 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010432 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010433 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010434 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010435 n2 = n3;
10436 }
10437 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010438 }
10439 }
10440 if (n1 == NULL) {
10441 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010442 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010443 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010444 n3->type = NSEMI;
10445 n3->nbinary.ch1 = n1;
10446 n3->nbinary.ch2 = n2;
10447 n1 = n3;
10448 }
10449 switch (tok) {
10450 case TBACKGND:
10451 case TSEMI:
10452 tok = readtoken();
10453 /* fall through */
10454 case TNL:
10455 if (tok == TNL) {
10456 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010457 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010458 return n1;
10459 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010460 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010461 }
Eric Andersenc470f442003-07-28 09:56:35 +000010462 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010463 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010464 return n1;
10465 break;
10466 case TEOF:
10467 if (heredoclist)
10468 parseheredoc();
10469 else
Eric Andersenc470f442003-07-28 09:56:35 +000010470 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010471 return n1;
10472 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010473 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010474 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010475 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010476 return n1;
10477 }
10478 }
10479}
10480
Eric Andersenc470f442003-07-28 09:56:35 +000010481static union node *
10482andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010483{
Eric Andersencb57d552001-06-28 07:25:16 +000010484 union node *n1, *n2, *n3;
10485 int t;
10486
Eric Andersencb57d552001-06-28 07:25:16 +000010487 n1 = pipeline();
10488 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010489 t = readtoken();
10490 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010491 t = NAND;
10492 } else if (t == TOR) {
10493 t = NOR;
10494 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010495 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010496 return n1;
10497 }
Eric Andersenc470f442003-07-28 09:56:35 +000010498 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010499 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010500 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010501 n3->type = t;
10502 n3->nbinary.ch1 = n1;
10503 n3->nbinary.ch2 = n2;
10504 n1 = n3;
10505 }
10506}
10507
Eric Andersenc470f442003-07-28 09:56:35 +000010508static union node *
10509pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010510{
Eric Andersencb57d552001-06-28 07:25:16 +000010511 union node *n1, *n2, *pipenode;
10512 struct nodelist *lp, *prev;
10513 int negate;
10514
10515 negate = 0;
10516 TRACE(("pipeline: entered\n"));
10517 if (readtoken() == TNOT) {
10518 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010519 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010520 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010521 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010522 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010523 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010524 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010525 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010526 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010527 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010528 pipenode->npipe.cmdlist = lp;
10529 lp->n = n1;
10530 do {
10531 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010532 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010533 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010534 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010535 prev->next = lp;
10536 } while (readtoken() == TPIPE);
10537 lp->next = NULL;
10538 n1 = pipenode;
10539 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010540 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010542 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010543 n2->type = NNOT;
10544 n2->nnot.com = n1;
10545 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010546 }
10547 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010548}
10549
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010550static union node *
10551makename(void)
10552{
10553 union node *n;
10554
Denis Vlasenko597906c2008-02-20 16:38:54 +000010555 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010556 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010557 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010558 n->narg.text = wordtext;
10559 n->narg.backquote = backquotelist;
10560 return n;
10561}
10562
10563static void
10564fixredir(union node *n, const char *text, int err)
10565{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010566 int fd;
10567
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010568 TRACE(("Fix redir %s %d\n", text, err));
10569 if (!err)
10570 n->ndup.vname = NULL;
10571
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010572 fd = bb_strtou(text, NULL, 10);
10573 if (!errno && fd >= 0)
10574 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010575 else if (LONE_DASH(text))
10576 n->ndup.dupfd = -1;
10577 else {
10578 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010579 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010580 n->ndup.vname = makename();
10581 }
10582}
10583
10584/*
10585 * Returns true if the text contains nothing to expand (no dollar signs
10586 * or backquotes).
10587 */
10588static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010589noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010590{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010591 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010592
Denys Vlasenkocd716832009-11-28 22:14:02 +010010593 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010594 if (c == CTLQUOTEMARK)
10595 continue;
10596 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010597 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010598 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010599 return 0;
10600 }
10601 return 1;
10602}
10603
10604static void
10605parsefname(void)
10606{
10607 union node *n = redirnode;
10608
10609 if (readtoken() != TWORD)
10610 raise_error_unexpected_syntax(-1);
10611 if (n->type == NHERE) {
10612 struct heredoc *here = heredoc;
10613 struct heredoc *p;
10614 int i;
10615
10616 if (quoteflag == 0)
10617 n->type = NXHERE;
10618 TRACE(("Here document %d\n", n->type));
10619 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010620 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010621 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010622 here->eofmark = wordtext;
10623 here->next = NULL;
10624 if (heredoclist == NULL)
10625 heredoclist = here;
10626 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010627 for (p = heredoclist; p->next; p = p->next)
10628 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010629 p->next = here;
10630 }
10631 } else if (n->type == NTOFD || n->type == NFROMFD) {
10632 fixredir(n, wordtext, 0);
10633 } else {
10634 n->nfile.fname = makename();
10635 }
10636}
Eric Andersencb57d552001-06-28 07:25:16 +000010637
Eric Andersenc470f442003-07-28 09:56:35 +000010638static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010639simplecmd(void)
10640{
10641 union node *args, **app;
10642 union node *n = NULL;
10643 union node *vars, **vpp;
10644 union node **rpp, *redir;
10645 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010646#if ENABLE_ASH_BASH_COMPAT
10647 smallint double_brackets_flag = 0;
10648#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010649
10650 args = NULL;
10651 app = &args;
10652 vars = NULL;
10653 vpp = &vars;
10654 redir = NULL;
10655 rpp = &redir;
10656
10657 savecheckkwd = CHKALIAS;
10658 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010659 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010660 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010661 t = readtoken();
10662 switch (t) {
10663#if ENABLE_ASH_BASH_COMPAT
10664 case TAND: /* "&&" */
10665 case TOR: /* "||" */
10666 if (!double_brackets_flag) {
10667 tokpushback = 1;
10668 goto out;
10669 }
10670 wordtext = (char *) (t == TAND ? "-a" : "-o");
10671#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010672 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010673 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010674 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010675 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010676 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010677#if ENABLE_ASH_BASH_COMPAT
10678 if (strcmp("[[", wordtext) == 0)
10679 double_brackets_flag = 1;
10680 else if (strcmp("]]", wordtext) == 0)
10681 double_brackets_flag = 0;
10682#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010683 n->narg.backquote = backquotelist;
10684 if (savecheckkwd && isassignment(wordtext)) {
10685 *vpp = n;
10686 vpp = &n->narg.next;
10687 } else {
10688 *app = n;
10689 app = &n->narg.next;
10690 savecheckkwd = 0;
10691 }
10692 break;
10693 case TREDIR:
10694 *rpp = n = redirnode;
10695 rpp = &n->nfile.next;
10696 parsefname(); /* read name of redirection file */
10697 break;
10698 case TLP:
10699 if (args && app == &args->narg.next
10700 && !vars && !redir
10701 ) {
10702 struct builtincmd *bcmd;
10703 const char *name;
10704
10705 /* We have a function */
10706 if (readtoken() != TRP)
10707 raise_error_unexpected_syntax(TRP);
10708 name = n->narg.text;
10709 if (!goodname(name)
10710 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10711 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010712 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010713 }
10714 n->type = NDEFUN;
10715 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10716 n->narg.next = parse_command();
10717 return n;
10718 }
10719 /* fall through */
10720 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010721 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010722 goto out;
10723 }
10724 }
10725 out:
10726 *app = NULL;
10727 *vpp = NULL;
10728 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010729 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010730 n->type = NCMD;
10731 n->ncmd.args = args;
10732 n->ncmd.assign = vars;
10733 n->ncmd.redirect = redir;
10734 return n;
10735}
10736
10737static union node *
10738parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010739{
Eric Andersencb57d552001-06-28 07:25:16 +000010740 union node *n1, *n2;
10741 union node *ap, **app;
10742 union node *cp, **cpp;
10743 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010744 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010745 int t;
10746
10747 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010748 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010749
Eric Andersencb57d552001-06-28 07:25:16 +000010750 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010751 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010752 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010753 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010754 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010755 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010756 n1->type = NIF;
10757 n1->nif.test = list(0);
10758 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010759 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010760 n1->nif.ifpart = list(0);
10761 n2 = n1;
10762 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010763 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010764 n2 = n2->nif.elsepart;
10765 n2->type = NIF;
10766 n2->nif.test = list(0);
10767 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010768 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010769 n2->nif.ifpart = list(0);
10770 }
10771 if (lasttoken == TELSE)
10772 n2->nif.elsepart = list(0);
10773 else {
10774 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010775 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010776 }
Eric Andersenc470f442003-07-28 09:56:35 +000010777 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010778 break;
10779 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010780 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010781 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010782 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010783 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010784 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010785 got = readtoken();
10786 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010787 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010788 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010789 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010790 }
10791 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010792 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010793 break;
10794 }
10795 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010796 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010797 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010798 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010799 n1->type = NFOR;
10800 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010801 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010802 if (readtoken() == TIN) {
10803 app = &ap;
10804 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010805 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010806 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010807 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010808 n2->narg.text = wordtext;
10809 n2->narg.backquote = backquotelist;
10810 *app = n2;
10811 app = &n2->narg.next;
10812 }
10813 *app = NULL;
10814 n1->nfor.args = ap;
10815 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010816 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010817 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010818 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010819 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010820 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010821 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010822 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010823 n1->nfor.args = n2;
10824 /*
10825 * Newline or semicolon here is optional (but note
10826 * that the original Bourne shell only allowed NL).
10827 */
10828 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010829 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010830 }
Eric Andersenc470f442003-07-28 09:56:35 +000010831 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010832 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010833 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010834 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010835 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010836 break;
10837 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010838 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010839 n1->type = NCASE;
10840 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010841 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010842 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010843 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010844 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010845 n2->narg.text = wordtext;
10846 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010847 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010848 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010849 } while (readtoken() == TNL);
10850 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010851 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010852 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010853 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010854 checkkwd = CHKNL | CHKKWD;
10855 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010856 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010857 if (lasttoken == TLP)
10858 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010859 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010860 cp->type = NCLIST;
10861 app = &cp->nclist.pattern;
10862 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010863 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010864 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010865 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010866 ap->narg.text = wordtext;
10867 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010868 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010869 break;
10870 app = &ap->narg.next;
10871 readtoken();
10872 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010873 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010874 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010875 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010876 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010877
Eric Andersenc470f442003-07-28 09:56:35 +000010878 cpp = &cp->nclist.next;
10879
10880 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010881 t = readtoken();
10882 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010883 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010884 raise_error_unexpected_syntax(TENDCASE);
10885 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010886 }
Eric Andersenc470f442003-07-28 09:56:35 +000010887 }
Eric Andersencb57d552001-06-28 07:25:16 +000010888 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010889 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010890 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010891 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010892 n1->type = NSUBSHELL;
10893 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010894 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010895 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010896 break;
10897 case TBEGIN:
10898 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010899 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010900 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010901 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010902 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010903 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010904 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010905 }
10906
Eric Andersenc470f442003-07-28 09:56:35 +000010907 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010908 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010909
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010910 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010911 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010912 checkkwd = CHKKWD | CHKALIAS;
10913 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010914 while (readtoken() == TREDIR) {
10915 *rpp = n2 = redirnode;
10916 rpp = &n2->nfile.next;
10917 parsefname();
10918 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010919 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010920 *rpp = NULL;
10921 if (redir) {
10922 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010923 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010924 n2->type = NREDIR;
10925 n2->nredir.n = n1;
10926 n1 = n2;
10927 }
10928 n1->nredir.redirect = redir;
10929 }
Eric Andersencb57d552001-06-28 07:25:16 +000010930 return n1;
10931}
10932
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010933#if ENABLE_ASH_BASH_COMPAT
10934static int decode_dollar_squote(void)
10935{
10936 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10937 int c, cnt;
10938 char *p;
10939 char buf[4];
10940
10941 c = pgetc();
10942 p = strchr(C_escapes, c);
10943 if (p) {
10944 buf[0] = c;
10945 p = buf;
10946 cnt = 3;
10947 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10948 do {
10949 c = pgetc();
10950 *++p = c;
10951 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10952 pungetc();
10953 } else if (c == 'x') { /* \xHH */
10954 do {
10955 c = pgetc();
10956 *++p = c;
10957 } while (isxdigit(c) && --cnt);
10958 pungetc();
10959 if (cnt == 3) { /* \x but next char is "bad" */
10960 c = 'x';
10961 goto unrecognized;
10962 }
10963 } else { /* simple seq like \\ or \t */
10964 p++;
10965 }
10966 *p = '\0';
10967 p = buf;
10968 c = bb_process_escape_sequence((void*)&p);
10969 } else { /* unrecognized "\z": print both chars unless ' or " */
10970 if (c != '\'' && c != '"') {
10971 unrecognized:
10972 c |= 0x100; /* "please encode \, then me" */
10973 }
10974 }
10975 return c;
10976}
10977#endif
10978
Eric Andersencb57d552001-06-28 07:25:16 +000010979/*
10980 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10981 * is not NULL, read a here document. In the latter case, eofmark is the
10982 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010010983 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000010984 * is the first character of the input token or document.
10985 *
10986 * Because C does not have internal subroutines, I have simulated them
10987 * using goto's to implement the subroutine linkage. The following macros
10988 * will run code that appears at the end of readtoken1.
10989 */
Eric Andersen2870d962001-07-02 17:27:21 +000010990#define CHECKEND() {goto checkend; checkend_return:;}
10991#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10992#define PARSESUB() {goto parsesub; parsesub_return:;}
10993#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10994#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10995#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010996static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010010997readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010998{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010999 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011000 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011001 char *out;
11002 int len;
11003 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011004 struct nodelist *bqlist;
11005 smallint quotef;
11006 smallint dblquote;
11007 smallint oldstyle;
11008 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011009#if ENABLE_ASH_EXPAND_PRMT
11010 smallint pssyntax; /* we are expanding a prompt string */
11011#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011012 int varnest; /* levels of variables expansion */
11013 int arinest; /* levels of arithmetic expansion */
11014 int parenlevel; /* levels of parens in arithmetic */
11015 int dqvarnest; /* levels of variables expansion within double quotes */
11016
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011017 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011018
Eric Andersencb57d552001-06-28 07:25:16 +000011019#if __GNUC__
11020 /* Avoid longjmp clobbering */
11021 (void) &out;
11022 (void) &quotef;
11023 (void) &dblquote;
11024 (void) &varnest;
11025 (void) &arinest;
11026 (void) &parenlevel;
11027 (void) &dqvarnest;
11028 (void) &oldstyle;
11029 (void) &prevsyntax;
11030 (void) &syntax;
11031#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011032 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011033 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011034 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011035 oldstyle = 0;
11036 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011037#if ENABLE_ASH_EXPAND_PRMT
11038 pssyntax = (syntax == PSSYNTAX);
11039 if (pssyntax)
11040 syntax = DQSYNTAX;
11041#endif
11042 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011043 varnest = 0;
11044 arinest = 0;
11045 parenlevel = 0;
11046 dqvarnest = 0;
11047
11048 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011049 loop:
11050 /* For each line, until end of word */
11051 {
Eric Andersenc470f442003-07-28 09:56:35 +000011052 CHECKEND(); /* set c to PEOF if at end of here document */
11053 for (;;) { /* until end of line or end of word */
11054 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011055 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011056 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000011057 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000011058 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011059 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011060 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000011061 if (doprompt)
11062 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000011063 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011064 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011065 case CWORD:
11066 USTPUTC(c, out);
11067 break;
11068 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000011069 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000011070 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011071#if ENABLE_ASH_BASH_COMPAT
11072 if (c == '\\' && bash_dollar_squote) {
11073 c = decode_dollar_squote();
11074 if (c & 0x100) {
11075 USTPUTC('\\', out);
11076 c = (unsigned char)c;
11077 }
11078 }
11079#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011080 USTPUTC(c, out);
11081 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011082 case CBACK: /* backslash */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011083 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011084 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000011085 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011086 USTPUTC('\\', out);
11087 pungetc();
11088 } else if (c == '\n') {
11089 if (doprompt)
11090 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000011091 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000011092#if ENABLE_ASH_EXPAND_PRMT
11093 if (c == '$' && pssyntax) {
11094 USTPUTC(CTLESC, out);
11095 USTPUTC('\\', out);
11096 }
11097#endif
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011098 /* Backslash is retained if we are in "str" and next char isn't special */
11099 if (dblquote
11100 && c != '\\'
11101 && c != '`'
11102 && c != '$'
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011103 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011104 ) {
11105 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011106 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011107 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011108 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000011109 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011110 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011111 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011112 }
11113 break;
11114 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011115 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011116 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000011117 if (eofmark == NULL) {
11118 USTPUTC(CTLQUOTEMARK, out);
11119 }
Eric Andersencb57d552001-06-28 07:25:16 +000011120 break;
11121 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011122 syntax = DQSYNTAX;
11123 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011124 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000011125 case CENDQUOTE:
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011126 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011127 if (eofmark != NULL && arinest == 0
11128 && varnest == 0
11129 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000011130 USTPUTC(c, out);
11131 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011132 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000011133 syntax = BASESYNTAX;
11134 dblquote = 0;
11135 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011136 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011137 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000011138 }
11139 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011140 case CVAR: /* '$' */
11141 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000011142 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011143 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000011144 if (varnest > 0) {
11145 varnest--;
11146 if (dqvarnest > 0) {
11147 dqvarnest--;
11148 }
11149 USTPUTC(CTLENDVAR, out);
11150 } else {
11151 USTPUTC(c, out);
11152 }
11153 break;
Mike Frysinger98c52642009-04-02 10:02:37 +000011154#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011155 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000011156 parenlevel++;
11157 USTPUTC(c, out);
11158 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011159 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000011160 if (parenlevel > 0) {
11161 USTPUTC(c, out);
11162 --parenlevel;
11163 } else {
11164 if (pgetc() == ')') {
11165 if (--arinest == 0) {
11166 USTPUTC(CTLENDARI, out);
11167 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011168 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011169 } else
11170 USTPUTC(')', out);
11171 } else {
11172 /*
11173 * unbalanced parens
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011174 * (don't 2nd guess - no error)
Eric Andersencb57d552001-06-28 07:25:16 +000011175 */
11176 pungetc();
11177 USTPUTC(')', out);
11178 }
11179 }
11180 break;
11181#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011182 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000011183 PARSEBACKQOLD();
11184 break;
Eric Andersen2870d962001-07-02 17:27:21 +000011185 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000011186 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011187 case CIGN:
11188 break;
11189 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000011190 if (varnest == 0) {
11191#if ENABLE_ASH_BASH_COMPAT
11192 if (c == '&') {
11193 if (pgetc() == '>')
11194 c = 0x100 + '>'; /* flag &> */
11195 pungetc();
11196 }
11197#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011198 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011199 }
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011200 IF_ASH_ALIAS(if (c != PEOA))
Eric Andersencb57d552001-06-28 07:25:16 +000011201 USTPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011202 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011203 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011204 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000011205 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011206 endword:
Mike Frysinger98c52642009-04-02 10:02:37 +000011207#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011208 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011209 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011210#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011211 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011212 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011213 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011214 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011215 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011216 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011217 }
11218 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011219 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011220 out = stackblock();
11221 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011222 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011223 && quotef == 0
11224 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011225 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011226 PARSEREDIR(); /* passed as params: out, c */
11227 lasttoken = TREDIR;
11228 return lasttoken;
11229 }
11230 /* else: non-number X seen, interpret it
11231 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011232 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011233 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011234 }
11235 quoteflag = quotef;
11236 backquotelist = bqlist;
11237 grabstackblock(len);
11238 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011239 lasttoken = TWORD;
11240 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011241/* end of readtoken routine */
11242
Eric Andersencb57d552001-06-28 07:25:16 +000011243/*
11244 * Check to see whether we are at the end of the here document. When this
11245 * is called, c is set to the first character of the next input line. If
11246 * we are at the end of the here document, this routine sets the c to PEOF.
11247 */
Eric Andersenc470f442003-07-28 09:56:35 +000011248checkend: {
11249 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011250#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011251 if (c == PEOA)
11252 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011253#endif
11254 if (striptabs) {
11255 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011256 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011257 }
Eric Andersenc470f442003-07-28 09:56:35 +000011258 }
11259 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011260 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011261 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011262
Eric Andersenc470f442003-07-28 09:56:35 +000011263 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011264 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11265 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011266 if (*p == '\n' && *q == '\0') {
11267 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011268 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011269 needprompt = doprompt;
11270 } else {
11271 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011272 }
11273 }
11274 }
11275 }
Eric Andersenc470f442003-07-28 09:56:35 +000011276 goto checkend_return;
11277}
Eric Andersencb57d552001-06-28 07:25:16 +000011278
Eric Andersencb57d552001-06-28 07:25:16 +000011279/*
11280 * Parse a redirection operator. The variable "out" points to a string
11281 * specifying the fd to be redirected. The variable "c" contains the
11282 * first character of the redirection operator.
11283 */
Eric Andersenc470f442003-07-28 09:56:35 +000011284parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011285 /* out is already checked to be a valid number or "" */
11286 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011287 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011288
Denis Vlasenko597906c2008-02-20 16:38:54 +000011289 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011290 if (c == '>') {
11291 np->nfile.fd = 1;
11292 c = pgetc();
11293 if (c == '>')
11294 np->type = NAPPEND;
11295 else if (c == '|')
11296 np->type = NCLOBBER;
11297 else if (c == '&')
11298 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011299 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011300 else {
11301 np->type = NTO;
11302 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011303 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011304 }
11305#if ENABLE_ASH_BASH_COMPAT
11306 else if (c == 0x100 + '>') { /* this flags &> redirection */
11307 np->nfile.fd = 1;
11308 pgetc(); /* this is '>', no need to check */
11309 np->type = NTO2;
11310 }
11311#endif
11312 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011313 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011314 c = pgetc();
11315 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011316 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011317 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011318 np = stzalloc(sizeof(struct nhere));
11319 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011320 }
11321 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011322 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011323 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011324 c = pgetc();
11325 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011326 heredoc->striptabs = 1;
11327 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011328 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011329 pungetc();
11330 }
11331 break;
11332
11333 case '&':
11334 np->type = NFROMFD;
11335 break;
11336
11337 case '>':
11338 np->type = NFROMTO;
11339 break;
11340
11341 default:
11342 np->type = NFROM;
11343 pungetc();
11344 break;
11345 }
Eric Andersencb57d552001-06-28 07:25:16 +000011346 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011347 if (fd >= 0)
11348 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011349 redirnode = np;
11350 goto parseredir_return;
11351}
Eric Andersencb57d552001-06-28 07:25:16 +000011352
Eric Andersencb57d552001-06-28 07:25:16 +000011353/*
11354 * Parse a substitution. At this point, we have read the dollar sign
11355 * and nothing else.
11356 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011357
11358/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11359 * (assuming ascii char codes, as the original implementation did) */
11360#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011361 (((unsigned)(c) - 33 < 32) \
11362 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011363parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011364 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011365 int typeloc;
11366 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011367
Eric Andersenc470f442003-07-28 09:56:35 +000011368 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011369 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011370 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011371 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011372#if ENABLE_ASH_BASH_COMPAT
11373 if (c == '\'')
11374 bash_dollar_squote = 1;
11375 else
11376#endif
11377 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011378 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011379 } else if (c == '(') {
11380 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011381 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011382#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011383 PARSEARITH();
11384#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011385 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011386#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011387 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011388 pungetc();
11389 PARSEBACKQNEW();
11390 }
11391 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011392 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011393 USTPUTC(CTLVAR, out);
11394 typeloc = out - (char *)stackblock();
11395 USTPUTC(VSNORMAL, out);
11396 subtype = VSNORMAL;
11397 if (c == '{') {
11398 c = pgetc();
11399 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011400 c = pgetc();
11401 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011402 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011403 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011404 subtype = VSLENGTH; /* ${#VAR} */
11405 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011406 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011407 }
Eric Andersenc470f442003-07-28 09:56:35 +000011408 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011409 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011410 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011411 do {
11412 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011413 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011414 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011415 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011416 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011417 do {
11418 STPUTC(c, out);
11419 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011420 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011421 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011422 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011423 USTPUTC(c, out);
11424 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011425 } else {
11426 badsub:
11427 raise_error_syntax("bad substitution");
11428 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011429 if (c != '}' && subtype == VSLENGTH) {
11430 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011431 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011432 }
Eric Andersencb57d552001-06-28 07:25:16 +000011433
Eric Andersenc470f442003-07-28 09:56:35 +000011434 STPUTC('=', out);
11435 flags = 0;
11436 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011437 /* ${VAR...} but not $VAR or ${#VAR} */
11438 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011439 switch (c) {
11440 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011441 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011442#if ENABLE_ASH_BASH_COMPAT
11443 if (c == ':' || c == '$' || isdigit(c)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011444 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011445 pungetc();
11446 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011447 }
11448#endif
11449 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011450 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011451 default: {
11452 static const char types[] ALIGN1 = "}-+?=";
11453 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011454 if (p == NULL)
11455 goto badsub;
11456 subtype = p - types + VSNORMAL;
11457 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011458 }
Eric Andersenc470f442003-07-28 09:56:35 +000011459 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011460 case '#': {
11461 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011462 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011463 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011464 if (c != cc)
11465 goto do_pungetc;
11466 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011467 break;
11468 }
11469#if ENABLE_ASH_BASH_COMPAT
11470 case '/':
11471 subtype = VSREPLACE;
11472 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011473 if (c != '/')
11474 goto do_pungetc;
11475 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011476 break;
11477#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011478 }
Eric Andersenc470f442003-07-28 09:56:35 +000011479 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011480 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011481 pungetc();
11482 }
11483 if (dblquote || arinest)
11484 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011485 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011486 if (subtype != VSNORMAL) {
11487 varnest++;
11488 if (dblquote || arinest) {
11489 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011490 }
11491 }
11492 }
Eric Andersenc470f442003-07-28 09:56:35 +000011493 goto parsesub_return;
11494}
Eric Andersencb57d552001-06-28 07:25:16 +000011495
Eric Andersencb57d552001-06-28 07:25:16 +000011496/*
11497 * Called to parse command substitutions. Newstyle is set if the command
11498 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11499 * list of commands (passed by reference), and savelen is the number of
11500 * characters on the top of the stack which must be preserved.
11501 */
Eric Andersenc470f442003-07-28 09:56:35 +000011502parsebackq: {
11503 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011504 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011505 union node *n;
11506 char *volatile str;
11507 struct jmploc jmploc;
11508 struct jmploc *volatile savehandler;
11509 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011510 smallint saveprompt = 0;
11511
Eric Andersencb57d552001-06-28 07:25:16 +000011512#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011513 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011514#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011515 savepbq = parsebackquote;
11516 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011517 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011518 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011519 exception_handler = savehandler;
11520 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011521 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011522 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011523 str = NULL;
11524 savelen = out - (char *)stackblock();
11525 if (savelen > 0) {
11526 str = ckmalloc(savelen);
11527 memcpy(str, stackblock(), savelen);
11528 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011529 savehandler = exception_handler;
11530 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011531 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011532 if (oldstyle) {
11533 /* We must read until the closing backquote, giving special
11534 treatment to some slashes, and then push the string and
11535 reread it as input, interpreting it normally. */
11536 char *pout;
11537 int pc;
11538 size_t psavelen;
11539 char *pstr;
11540
11541
11542 STARTSTACKSTR(pout);
11543 for (;;) {
11544 if (needprompt) {
11545 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011546 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011547 pc = pgetc();
11548 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011549 case '`':
11550 goto done;
11551
11552 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011553 pc = pgetc();
11554 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011555 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011556 if (doprompt)
11557 setprompt(2);
11558 /*
11559 * If eating a newline, avoid putting
11560 * the newline into the new character
11561 * stream (via the STPUTC after the
11562 * switch).
11563 */
11564 continue;
11565 }
11566 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011567 && (!dblquote || pc != '"')
11568 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011569 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011570 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011571 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011572 break;
11573 }
11574 /* fall through */
11575
11576 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011577 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011578 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011579 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011580
11581 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011582 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011583 needprompt = doprompt;
11584 break;
11585
11586 default:
11587 break;
11588 }
11589 STPUTC(pc, pout);
11590 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011591 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011592 STPUTC('\0', pout);
11593 psavelen = pout - (char *)stackblock();
11594 if (psavelen > 0) {
11595 pstr = grabstackstr(pout);
11596 setinputstring(pstr);
11597 }
11598 }
11599 nlpp = &bqlist;
11600 while (*nlpp)
11601 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011602 *nlpp = stzalloc(sizeof(**nlpp));
11603 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011604 parsebackquote = oldstyle;
11605
11606 if (oldstyle) {
11607 saveprompt = doprompt;
11608 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011609 }
11610
Eric Andersenc470f442003-07-28 09:56:35 +000011611 n = list(2);
11612
11613 if (oldstyle)
11614 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011615 else if (readtoken() != TRP)
11616 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011617
11618 (*nlpp)->n = n;
11619 if (oldstyle) {
11620 /*
11621 * Start reading from old file again, ignoring any pushed back
11622 * tokens left from the backquote parsing
11623 */
11624 popfile();
11625 tokpushback = 0;
11626 }
11627 while (stackblocksize() <= savelen)
11628 growstackblock();
11629 STARTSTACKSTR(out);
11630 if (str) {
11631 memcpy(out, str, savelen);
11632 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011633 INT_OFF;
11634 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011635 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011636 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011637 }
11638 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011639 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011640 if (arinest || dblquote)
11641 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11642 else
11643 USTPUTC(CTLBACKQ, out);
11644 if (oldstyle)
11645 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011646 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011647}
11648
Mike Frysinger98c52642009-04-02 10:02:37 +000011649#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011650/*
11651 * Parse an arithmetic expansion (indicate start of one and set state)
11652 */
Eric Andersenc470f442003-07-28 09:56:35 +000011653parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011654 if (++arinest == 1) {
11655 prevsyntax = syntax;
11656 syntax = ARISYNTAX;
11657 USTPUTC(CTLARI, out);
11658 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011659 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011660 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011661 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011662 } else {
11663 /*
11664 * we collapse embedded arithmetic expansion to
11665 * parenthesis, which should be equivalent
11666 */
11667 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011668 }
Eric Andersenc470f442003-07-28 09:56:35 +000011669 goto parsearith_return;
11670}
11671#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011672
Eric Andersenc470f442003-07-28 09:56:35 +000011673} /* end of readtoken */
11674
Eric Andersencb57d552001-06-28 07:25:16 +000011675/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011676 * Read the next input token.
11677 * If the token is a word, we set backquotelist to the list of cmds in
11678 * backquotes. We set quoteflag to true if any part of the word was
11679 * quoted.
11680 * If the token is TREDIR, then we set redirnode to a structure containing
11681 * the redirection.
11682 * In all cases, the variable startlinno is set to the number of the line
11683 * on which the token starts.
11684 *
11685 * [Change comment: here documents and internal procedures]
11686 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11687 * word parsing code into a separate routine. In this case, readtoken
11688 * doesn't need to have any internal procedures, but parseword does.
11689 * We could also make parseoperator in essence the main routine, and
11690 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011691 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011692#define NEW_xxreadtoken
11693#ifdef NEW_xxreadtoken
11694/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011695static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011696 '\n', '(', ')', /* singles */
11697 '&', '|', ';', /* doubles */
11698 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011699};
Eric Andersencb57d552001-06-28 07:25:16 +000011700
Denis Vlasenko834dee72008-10-07 09:18:30 +000011701#define xxreadtoken_singles 3
11702#define xxreadtoken_doubles 3
11703
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011704static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011705 TNL, TLP, TRP, /* only single occurrence allowed */
11706 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11707 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011708 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011709};
11710
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011711static int
11712xxreadtoken(void)
11713{
11714 int c;
11715
11716 if (tokpushback) {
11717 tokpushback = 0;
11718 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011719 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011720 if (needprompt) {
11721 setprompt(2);
11722 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011723 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011724 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011725 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011726 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011727 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011728
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011729 if (c == '#') {
11730 while ((c = pgetc()) != '\n' && c != PEOF)
11731 continue;
11732 pungetc();
11733 } else if (c == '\\') {
11734 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011735 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011736 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011737 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011738 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011739 if (doprompt)
11740 setprompt(2);
11741 } else {
11742 const char *p;
11743
11744 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11745 if (c != PEOF) {
11746 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011747 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011748 needprompt = doprompt;
11749 }
11750
11751 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011752 if (p == NULL)
11753 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011754
Denis Vlasenko834dee72008-10-07 09:18:30 +000011755 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11756 int cc = pgetc();
11757 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011758 p += xxreadtoken_doubles + 1;
11759 } else {
11760 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011761#if ENABLE_ASH_BASH_COMPAT
11762 if (c == '&' && cc == '>') /* &> */
11763 break; /* return readtoken1(...) */
11764#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011765 }
11766 }
11767 }
11768 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11769 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011770 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011771 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011772
11773 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011774}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011775#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011776#define RETURN(token) return lasttoken = token
11777static int
11778xxreadtoken(void)
11779{
11780 int c;
11781
11782 if (tokpushback) {
11783 tokpushback = 0;
11784 return lasttoken;
11785 }
11786 if (needprompt) {
11787 setprompt(2);
11788 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011789 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011790 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011791 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011792 switch (c) {
11793 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011794 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011795 continue;
11796 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011797 while ((c = pgetc()) != '\n' && c != PEOF)
11798 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011799 pungetc();
11800 continue;
11801 case '\\':
11802 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011803 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011804 if (doprompt)
11805 setprompt(2);
11806 continue;
11807 }
11808 pungetc();
11809 goto breakloop;
11810 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011811 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011812 needprompt = doprompt;
11813 RETURN(TNL);
11814 case PEOF:
11815 RETURN(TEOF);
11816 case '&':
11817 if (pgetc() == '&')
11818 RETURN(TAND);
11819 pungetc();
11820 RETURN(TBACKGND);
11821 case '|':
11822 if (pgetc() == '|')
11823 RETURN(TOR);
11824 pungetc();
11825 RETURN(TPIPE);
11826 case ';':
11827 if (pgetc() == ';')
11828 RETURN(TENDCASE);
11829 pungetc();
11830 RETURN(TSEMI);
11831 case '(':
11832 RETURN(TLP);
11833 case ')':
11834 RETURN(TRP);
11835 default:
11836 goto breakloop;
11837 }
11838 }
11839 breakloop:
11840 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11841#undef RETURN
11842}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011843#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011844
11845static int
11846readtoken(void)
11847{
11848 int t;
11849#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011850 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011851#endif
11852
11853#if ENABLE_ASH_ALIAS
11854 top:
11855#endif
11856
11857 t = xxreadtoken();
11858
11859 /*
11860 * eat newlines
11861 */
11862 if (checkkwd & CHKNL) {
11863 while (t == TNL) {
11864 parseheredoc();
11865 t = xxreadtoken();
11866 }
11867 }
11868
11869 if (t != TWORD || quoteflag) {
11870 goto out;
11871 }
11872
11873 /*
11874 * check for keywords
11875 */
11876 if (checkkwd & CHKKWD) {
11877 const char *const *pp;
11878
11879 pp = findkwd(wordtext);
11880 if (pp) {
11881 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011882 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011883 goto out;
11884 }
11885 }
11886
11887 if (checkkwd & CHKALIAS) {
11888#if ENABLE_ASH_ALIAS
11889 struct alias *ap;
11890 ap = lookupalias(wordtext, 1);
11891 if (ap != NULL) {
11892 if (*ap->val) {
11893 pushstring(ap->val, ap);
11894 }
11895 goto top;
11896 }
11897#endif
11898 }
11899 out:
11900 checkkwd = 0;
11901#if DEBUG
11902 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011903 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011904 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011905 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011906#endif
11907 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011908}
11909
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011910static char
11911peektoken(void)
11912{
11913 int t;
11914
11915 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011916 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011917 return tokname_array[t][0];
11918}
Eric Andersencb57d552001-06-28 07:25:16 +000011919
11920/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011921 * Read and parse a command. Returns NODE_EOF on end of file.
11922 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011923 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011924static union node *
11925parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011926{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011927 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011928
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011929 tokpushback = 0;
11930 doprompt = interact;
11931 if (doprompt)
11932 setprompt(doprompt);
11933 needprompt = 0;
11934 t = readtoken();
11935 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011936 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011937 if (t == TNL)
11938 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011939 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011940 return list(1);
11941}
11942
11943/*
11944 * Input any here documents.
11945 */
11946static void
11947parseheredoc(void)
11948{
11949 struct heredoc *here;
11950 union node *n;
11951
11952 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011953 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011954
11955 while (here) {
11956 if (needprompt) {
11957 setprompt(2);
11958 }
11959 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11960 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011961 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011962 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011963 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011964 n->narg.text = wordtext;
11965 n->narg.backquote = backquotelist;
11966 here->here->nhere.doc = n;
11967 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011968 }
Eric Andersencb57d552001-06-28 07:25:16 +000011969}
11970
11971
11972/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011973 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011974 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011975#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011976static const char *
11977expandstr(const char *ps)
11978{
11979 union node n;
11980
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011981 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11982 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011983 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011984 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011985 popfile();
11986
11987 n.narg.type = NARG;
11988 n.narg.next = NULL;
11989 n.narg.text = wordtext;
11990 n.narg.backquote = backquotelist;
11991
11992 expandarg(&n, NULL, 0);
11993 return stackblock();
11994}
11995#endif
11996
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011997/*
11998 * Execute a command or commands contained in a string.
11999 */
12000static int
12001evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012002{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012003 union node *n;
12004 struct stackmark smark;
12005 int skip;
12006
12007 setinputstring(s);
12008 setstackmark(&smark);
12009
12010 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012011 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012012 evaltree(n, 0);
12013 popstackmark(&smark);
12014 skip = evalskip;
12015 if (skip)
12016 break;
12017 }
12018 popfile();
12019
12020 skip &= mask;
12021 evalskip = skip;
12022 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012023}
12024
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012025/*
12026 * The eval command.
12027 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012028static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012029evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012030{
12031 char *p;
12032 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012033
Denis Vlasenko68404f12008-03-17 09:00:54 +000012034 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012035 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012036 argv += 2;
12037 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012038 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012039 for (;;) {
12040 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012041 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012042 if (p == NULL)
12043 break;
12044 STPUTC(' ', concat);
12045 }
12046 STPUTC('\0', concat);
12047 p = grabstackstr(concat);
12048 }
12049 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012050 }
12051 return exitstatus;
12052}
12053
12054/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012055 * Read and execute commands.
12056 * "Top" is nonzero for the top level command loop;
12057 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012058 */
12059static int
12060cmdloop(int top)
12061{
12062 union node *n;
12063 struct stackmark smark;
12064 int inter;
12065 int numeof = 0;
12066
12067 TRACE(("cmdloop(%d) called\n", top));
12068 for (;;) {
12069 int skip;
12070
12071 setstackmark(&smark);
12072#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012073 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012074 showjobs(stderr, SHOW_CHANGED);
12075#endif
12076 inter = 0;
12077 if (iflag && top) {
12078 inter++;
12079#if ENABLE_ASH_MAIL
12080 chkmail();
12081#endif
12082 }
12083 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012084#if DEBUG
12085 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012086 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012087#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012088 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012089 if (!top || numeof >= 50)
12090 break;
12091 if (!stoppedjobs()) {
12092 if (!Iflag)
12093 break;
12094 out2str("\nUse \"exit\" to leave shell.\n");
12095 }
12096 numeof++;
12097 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012098 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12099 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012100 numeof = 0;
12101 evaltree(n, 0);
12102 }
12103 popstackmark(&smark);
12104 skip = evalskip;
12105
12106 if (skip) {
12107 evalskip = 0;
12108 return skip & SKIPEVAL;
12109 }
12110 }
12111 return 0;
12112}
12113
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012114/*
12115 * Take commands from a file. To be compatible we should do a path
12116 * search for the file, which is necessary to find sub-commands.
12117 */
12118static char *
12119find_dot_file(char *name)
12120{
12121 char *fullname;
12122 const char *path = pathval();
12123 struct stat statb;
12124
12125 /* don't try this for absolute or relative paths */
12126 if (strchr(name, '/'))
12127 return name;
12128
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012129 /* IIRC standards do not say whether . is to be searched.
12130 * And it is even smaller this way, making it unconditional for now:
12131 */
12132 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12133 fullname = name;
12134 goto try_cur_dir;
12135 }
12136
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012137 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012138 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012139 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12140 /*
12141 * Don't bother freeing here, since it will
12142 * be freed by the caller.
12143 */
12144 return fullname;
12145 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012146 if (fullname != name)
12147 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012148 }
12149
12150 /* not found in the PATH */
12151 ash_msg_and_raise_error("%s: not found", name);
12152 /* NOTREACHED */
12153}
12154
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012155static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012156dotcmd(int argc, char **argv)
12157{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012158 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012159 struct strlist *sp;
12160 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012161
12162 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012163 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012164
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012165 if (!argv[1]) {
12166 /* bash says: "bash: .: filename argument required" */
12167 return 2; /* bash compat */
12168 }
12169
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012170 /* "false; . empty_file; echo $?" should print 0, not 1: */
12171 exitstatus = 0;
12172
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012173 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012174
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012175 argv += 2;
12176 argc -= 2;
12177 if (argc) { /* argc > 0, argv[0] != NULL */
12178 saveparam = shellparam;
12179 shellparam.malloced = 0;
12180 shellparam.nparam = argc;
12181 shellparam.p = argv;
12182 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012183
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012184 setinputfile(fullname, INPUT_PUSH_FILE);
12185 commandname = fullname;
12186 cmdloop(0);
12187 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012188
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012189 if (argc) {
12190 freeparam(&shellparam);
12191 shellparam = saveparam;
12192 };
12193
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012194 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012195}
12196
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012197static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012198exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012199{
12200 if (stoppedjobs())
12201 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012202 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012203 exitstatus = number(argv[1]);
12204 raise_exception(EXEXIT);
12205 /* NOTREACHED */
12206}
12207
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012208/*
12209 * Read a file containing shell functions.
12210 */
12211static void
12212readcmdfile(char *name)
12213{
12214 setinputfile(name, INPUT_PUSH_FILE);
12215 cmdloop(0);
12216 popfile();
12217}
12218
12219
Denis Vlasenkocc571512007-02-23 21:10:35 +000012220/* ============ find_command inplementation */
12221
12222/*
12223 * Resolve a command name. If you change this routine, you may have to
12224 * change the shellexec routine as well.
12225 */
12226static void
12227find_command(char *name, struct cmdentry *entry, int act, const char *path)
12228{
12229 struct tblentry *cmdp;
12230 int idx;
12231 int prev;
12232 char *fullname;
12233 struct stat statb;
12234 int e;
12235 int updatetbl;
12236 struct builtincmd *bcmd;
12237
12238 /* If name contains a slash, don't use PATH or hash table */
12239 if (strchr(name, '/') != NULL) {
12240 entry->u.index = -1;
12241 if (act & DO_ABS) {
12242 while (stat(name, &statb) < 0) {
12243#ifdef SYSV
12244 if (errno == EINTR)
12245 continue;
12246#endif
12247 entry->cmdtype = CMDUNKNOWN;
12248 return;
12249 }
12250 }
12251 entry->cmdtype = CMDNORMAL;
12252 return;
12253 }
12254
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012255/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012256
12257 updatetbl = (path == pathval());
12258 if (!updatetbl) {
12259 act |= DO_ALTPATH;
12260 if (strstr(path, "%builtin") != NULL)
12261 act |= DO_ALTBLTIN;
12262 }
12263
12264 /* If name is in the table, check answer will be ok */
12265 cmdp = cmdlookup(name, 0);
12266 if (cmdp != NULL) {
12267 int bit;
12268
12269 switch (cmdp->cmdtype) {
12270 default:
12271#if DEBUG
12272 abort();
12273#endif
12274 case CMDNORMAL:
12275 bit = DO_ALTPATH;
12276 break;
12277 case CMDFUNCTION:
12278 bit = DO_NOFUNC;
12279 break;
12280 case CMDBUILTIN:
12281 bit = DO_ALTBLTIN;
12282 break;
12283 }
12284 if (act & bit) {
12285 updatetbl = 0;
12286 cmdp = NULL;
12287 } else if (cmdp->rehash == 0)
12288 /* if not invalidated by cd, we're done */
12289 goto success;
12290 }
12291
12292 /* If %builtin not in path, check for builtin next */
12293 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012294 if (bcmd) {
12295 if (IS_BUILTIN_REGULAR(bcmd))
12296 goto builtin_success;
12297 if (act & DO_ALTPATH) {
12298 if (!(act & DO_ALTBLTIN))
12299 goto builtin_success;
12300 } else if (builtinloc <= 0) {
12301 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012302 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012303 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012304
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012305#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012306 {
12307 int applet_no = find_applet_by_name(name);
12308 if (applet_no >= 0) {
12309 entry->cmdtype = CMDNORMAL;
12310 entry->u.index = -2 - applet_no;
12311 return;
12312 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012313 }
12314#endif
12315
Denis Vlasenkocc571512007-02-23 21:10:35 +000012316 /* We have to search path. */
12317 prev = -1; /* where to start */
12318 if (cmdp && cmdp->rehash) { /* doing a rehash */
12319 if (cmdp->cmdtype == CMDBUILTIN)
12320 prev = builtinloc;
12321 else
12322 prev = cmdp->param.index;
12323 }
12324
12325 e = ENOENT;
12326 idx = -1;
12327 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012328 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012329 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012330 /* NB: code below will still use fullname
12331 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012332 idx++;
12333 if (pathopt) {
12334 if (prefix(pathopt, "builtin")) {
12335 if (bcmd)
12336 goto builtin_success;
12337 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012338 }
12339 if ((act & DO_NOFUNC)
12340 || !prefix(pathopt, "func")
12341 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012342 continue;
12343 }
12344 }
12345 /* if rehash, don't redo absolute path names */
12346 if (fullname[0] == '/' && idx <= prev) {
12347 if (idx < prev)
12348 continue;
12349 TRACE(("searchexec \"%s\": no change\n", name));
12350 goto success;
12351 }
12352 while (stat(fullname, &statb) < 0) {
12353#ifdef SYSV
12354 if (errno == EINTR)
12355 continue;
12356#endif
12357 if (errno != ENOENT && errno != ENOTDIR)
12358 e = errno;
12359 goto loop;
12360 }
12361 e = EACCES; /* if we fail, this will be the error */
12362 if (!S_ISREG(statb.st_mode))
12363 continue;
12364 if (pathopt) { /* this is a %func directory */
12365 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012366 /* NB: stalloc will return space pointed by fullname
12367 * (because we don't have any intervening allocations
12368 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012369 readcmdfile(fullname);
12370 cmdp = cmdlookup(name, 0);
12371 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12372 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12373 stunalloc(fullname);
12374 goto success;
12375 }
12376 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12377 if (!updatetbl) {
12378 entry->cmdtype = CMDNORMAL;
12379 entry->u.index = idx;
12380 return;
12381 }
12382 INT_OFF;
12383 cmdp = cmdlookup(name, 1);
12384 cmdp->cmdtype = CMDNORMAL;
12385 cmdp->param.index = idx;
12386 INT_ON;
12387 goto success;
12388 }
12389
12390 /* We failed. If there was an entry for this command, delete it */
12391 if (cmdp && updatetbl)
12392 delete_cmd_entry();
12393 if (act & DO_ERR)
12394 ash_msg("%s: %s", name, errmsg(e, "not found"));
12395 entry->cmdtype = CMDUNKNOWN;
12396 return;
12397
12398 builtin_success:
12399 if (!updatetbl) {
12400 entry->cmdtype = CMDBUILTIN;
12401 entry->u.cmd = bcmd;
12402 return;
12403 }
12404 INT_OFF;
12405 cmdp = cmdlookup(name, 1);
12406 cmdp->cmdtype = CMDBUILTIN;
12407 cmdp->param.cmd = bcmd;
12408 INT_ON;
12409 success:
12410 cmdp->rehash = 0;
12411 entry->cmdtype = cmdp->cmdtype;
12412 entry->u = cmdp->param;
12413}
12414
12415
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012416/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012417
Eric Andersencb57d552001-06-28 07:25:16 +000012418/*
Eric Andersencb57d552001-06-28 07:25:16 +000012419 * The trap builtin.
12420 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012421static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012422trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012423{
12424 char *action;
12425 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012426 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012427
Eric Andersenc470f442003-07-28 09:56:35 +000012428 nextopt(nullstr);
12429 ap = argptr;
12430 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012431 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012432 char *tr = trap_ptr[signo];
12433 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012434 /* note: bash adds "SIG", but only if invoked
12435 * as "bash". If called as "sh", or if set -o posix,
12436 * then it prints short signal names.
12437 * We are printing short names: */
12438 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012439 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012440 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012441 /* trap_ptr != trap only if we are in special-cased `trap` code.
12442 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012443 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012444 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012445 }
12446 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012447 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012448 if (trap_ptr != trap) {
12449 free(trap_ptr);
12450 trap_ptr = trap;
12451 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012452 */
Eric Andersencb57d552001-06-28 07:25:16 +000012453 return 0;
12454 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012455
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012456 action = NULL;
12457 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012458 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012459 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012460 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012461 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012462 if (signo < 0) {
12463 /* Mimic bash message exactly */
12464 ash_msg("%s: invalid signal specification", *ap);
12465 exitcode = 1;
12466 goto next;
12467 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012468 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012469 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012470 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012471 action = NULL;
12472 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012473 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012474 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012475 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012476 if (action)
12477 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012478 trap[signo] = action;
12479 if (signo != 0)
12480 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012481 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012482 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012483 ap++;
12484 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012485 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012486}
12487
Eric Andersenc470f442003-07-28 09:56:35 +000012488
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012489/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012490
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012491#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012492/*
12493 * Lists available builtins
12494 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012495static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012496helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012497{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012498 unsigned col;
12499 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012500
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012501 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012502 "Built-in commands:\n"
12503 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012504 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012505 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012506 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012507 if (col > 60) {
12508 out1fmt("\n");
12509 col = 0;
12510 }
12511 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012512#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012513 {
12514 const char *a = applet_names;
12515 while (*a) {
12516 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12517 if (col > 60) {
12518 out1fmt("\n");
12519 col = 0;
12520 }
12521 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012522 }
12523 }
12524#endif
12525 out1fmt("\n\n");
12526 return EXIT_SUCCESS;
12527}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012528#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012529
Eric Andersencb57d552001-06-28 07:25:16 +000012530/*
Eric Andersencb57d552001-06-28 07:25:16 +000012531 * The export and readonly commands.
12532 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012533static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012534exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012535{
12536 struct var *vp;
12537 char *name;
12538 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012539 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012540 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012541
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012542 if (nextopt("p") != 'p') {
12543 aptr = argptr;
12544 name = *aptr;
12545 if (name) {
12546 do {
12547 p = strchr(name, '=');
12548 if (p != NULL) {
12549 p++;
12550 } else {
12551 vp = *findvar(hashvar(name), name);
12552 if (vp) {
12553 vp->flags |= flag;
12554 continue;
12555 }
Eric Andersencb57d552001-06-28 07:25:16 +000012556 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012557 setvar(name, p, flag);
12558 } while ((name = *++aptr) != NULL);
12559 return 0;
12560 }
Eric Andersencb57d552001-06-28 07:25:16 +000012561 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012562 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012563 return 0;
12564}
12565
Eric Andersencb57d552001-06-28 07:25:16 +000012566/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012567 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012568 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012569static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012570unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012571{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012572 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012573
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012574 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012575 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012576 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012577}
12578
Eric Andersencb57d552001-06-28 07:25:16 +000012579/*
Eric Andersencb57d552001-06-28 07:25:16 +000012580 * The unset builtin command. We unset the function before we unset the
12581 * variable to allow a function to be unset when there is a readonly variable
12582 * with the same name.
12583 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012584static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012585unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012586{
12587 char **ap;
12588 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012589 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012590 int ret = 0;
12591
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012592 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012593 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012594 }
Eric Andersencb57d552001-06-28 07:25:16 +000012595
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012596 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012597 if (flag != 'f') {
12598 i = unsetvar(*ap);
12599 ret |= i;
12600 if (!(i & 2))
12601 continue;
12602 }
12603 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012604 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012605 }
Eric Andersenc470f442003-07-28 09:56:35 +000012606 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012607}
12608
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012609static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012610 ' ', offsetof(struct tms, tms_utime),
12611 '\n', offsetof(struct tms, tms_stime),
12612 ' ', offsetof(struct tms, tms_cutime),
12613 '\n', offsetof(struct tms, tms_cstime),
12614 0
12615};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012616static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012617timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012618{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012619 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012620 const unsigned char *p;
12621 struct tms buf;
12622
12623 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012624 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012625
12626 p = timescmd_str;
12627 do {
12628 t = *(clock_t *)(((char *) &buf) + p[1]);
12629 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012630 t = t % clk_tck;
12631 out1fmt("%lum%lu.%03lus%c",
12632 s / 60, s % 60,
12633 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012634 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012635 p += 2;
12636 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012637
Eric Andersencb57d552001-06-28 07:25:16 +000012638 return 0;
12639}
12640
Mike Frysinger98c52642009-04-02 10:02:37 +000012641#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012642/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012643 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012644 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012645 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012646 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012647 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012648static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012649letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012650{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012651 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012652
Denis Vlasenko68404f12008-03-17 09:00:54 +000012653 argv++;
12654 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012655 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012656 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012657 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012658 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012659
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012660 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012661}
Eric Andersenc470f442003-07-28 09:56:35 +000012662#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012663
Eric Andersenc470f442003-07-28 09:56:35 +000012664/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012665 * The read builtin. Options:
12666 * -r Do not interpret '\' specially
12667 * -s Turn off echo (tty only)
12668 * -n NCHARS Read NCHARS max
12669 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12670 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12671 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012672 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012673 * TODO: bash also has:
12674 * -a ARRAY Read into array[0],[1],etc
12675 * -d DELIM End on DELIM char, not newline
12676 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012677 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012678static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012679readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012680{
Denys Vlasenko73067272010-01-12 22:11:24 +010012681 char *opt_n = NULL;
12682 char *opt_p = NULL;
12683 char *opt_t = NULL;
12684 char *opt_u = NULL;
12685 int read_flags = 0;
12686 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012687 int i;
12688
Denys Vlasenko73067272010-01-12 22:11:24 +010012689 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012690 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012691 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012692 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012693 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012694 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012695 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012696 break;
12697 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012698 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012699 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012700 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012701 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012702 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012703 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012704 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012705 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012706 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012707 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012708 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012709 default:
12710 break;
12711 }
Eric Andersenc470f442003-07-28 09:56:35 +000012712 }
Paul Fox02eb9342005-09-07 16:56:02 +000012713
Denys Vlasenko03dad222010-01-12 23:29:57 +010012714 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012715 argptr,
12716 bltinlookup("IFS"), /* can be NULL */
12717 read_flags,
12718 opt_n,
12719 opt_p,
12720 opt_t,
12721 opt_u
12722 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012723
Denys Vlasenko73067272010-01-12 22:11:24 +010012724 if ((uintptr_t)r > 1)
12725 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012726
Denys Vlasenko73067272010-01-12 22:11:24 +010012727 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012728}
12729
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012730static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012731umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012732{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012733 static const char permuser[3] ALIGN1 = "ugo";
12734 static const char permmode[3] ALIGN1 = "rwx";
12735 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012736 S_IRUSR, S_IWUSR, S_IXUSR,
12737 S_IRGRP, S_IWGRP, S_IXGRP,
12738 S_IROTH, S_IWOTH, S_IXOTH
12739 };
12740
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012741 /* TODO: use bb_parse_mode() instead */
12742
Eric Andersenc470f442003-07-28 09:56:35 +000012743 char *ap;
12744 mode_t mask;
12745 int i;
12746 int symbolic_mode = 0;
12747
12748 while (nextopt("S") != '\0') {
12749 symbolic_mode = 1;
12750 }
12751
Denis Vlasenkob012b102007-02-19 22:43:01 +000012752 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012753 mask = umask(0);
12754 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012755 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012756
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012757 ap = *argptr;
12758 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012759 if (symbolic_mode) {
12760 char buf[18];
12761 char *p = buf;
12762
12763 for (i = 0; i < 3; i++) {
12764 int j;
12765
12766 *p++ = permuser[i];
12767 *p++ = '=';
12768 for (j = 0; j < 3; j++) {
12769 if ((mask & permmask[3 * i + j]) == 0) {
12770 *p++ = permmode[j];
12771 }
12772 }
12773 *p++ = ',';
12774 }
12775 *--p = 0;
12776 puts(buf);
12777 } else {
12778 out1fmt("%.4o\n", mask);
12779 }
12780 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012781 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012782 mask = 0;
12783 do {
12784 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012785 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012786 mask = (mask << 3) + (*ap - '0');
12787 } while (*++ap != '\0');
12788 umask(mask);
12789 } else {
12790 mask = ~mask & 0777;
12791 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012792 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012793 }
12794 umask(~mask & 0777);
12795 }
12796 }
12797 return 0;
12798}
12799
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012800static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012801ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012802{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012803 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012804}
12805
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012806/* ============ main() and helpers */
12807
12808/*
12809 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012810 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012811static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012812static void
12813exitshell(void)
12814{
12815 struct jmploc loc;
12816 char *p;
12817 int status;
12818
12819 status = exitstatus;
12820 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12821 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012822 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012823/* dash bug: it just does _exit(exitstatus) here
12824 * but we have to do setjobctl(0) first!
12825 * (bug is still not fixed in dash-0.5.3 - if you run dash
12826 * under Midnight Commander, on exit from dash MC is backgrounded) */
12827 status = exitstatus;
12828 goto out;
12829 }
12830 exception_handler = &loc;
12831 p = trap[0];
12832 if (p) {
12833 trap[0] = NULL;
12834 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012835 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012836 }
12837 flush_stdout_stderr();
12838 out:
12839 setjobctl(0);
12840 _exit(status);
12841 /* NOTREACHED */
12842}
12843
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012844static void
12845init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012846{
12847 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012848 /* we will never free this */
12849 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012850
12851 /* from trap.c: */
12852 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012853 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12854 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12855 */
12856 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012857
12858 /* from var.c: */
12859 {
12860 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012861 const char *p;
12862 struct stat st1, st2;
12863
12864 initvar();
12865 for (envp = environ; envp && *envp; envp++) {
12866 if (strchr(*envp, '=')) {
12867 setvareq(*envp, VEXPORT|VTEXTFIXED);
12868 }
12869 }
12870
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012871 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012872
12873 p = lookupvar("PWD");
12874 if (p)
12875 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12876 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12877 p = '\0';
12878 setpwd(p, 0);
12879 }
12880}
12881
12882/*
12883 * Process the shell command line arguments.
12884 */
12885static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012886procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012887{
12888 int i;
12889 const char *xminusc;
12890 char **xargv;
12891
12892 xargv = argv;
12893 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012894 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012895 xargv++;
12896 for (i = 0; i < NOPTS; i++)
12897 optlist[i] = 2;
12898 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012899 if (options(1)) {
12900 /* it already printed err message */
12901 raise_exception(EXERROR);
12902 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012903 xargv = argptr;
12904 xminusc = minusc;
12905 if (*xargv == NULL) {
12906 if (xminusc)
12907 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12908 sflag = 1;
12909 }
12910 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12911 iflag = 1;
12912 if (mflag == 2)
12913 mflag = iflag;
12914 for (i = 0; i < NOPTS; i++)
12915 if (optlist[i] == 2)
12916 optlist[i] = 0;
12917#if DEBUG == 2
12918 debug = 1;
12919#endif
12920 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12921 if (xminusc) {
12922 minusc = *xargv++;
12923 if (*xargv)
12924 goto setarg0;
12925 } else if (!sflag) {
12926 setinputfile(*xargv, 0);
12927 setarg0:
12928 arg0 = *xargv++;
12929 commandname = arg0;
12930 }
12931
12932 shellparam.p = xargv;
12933#if ENABLE_ASH_GETOPTS
12934 shellparam.optind = 1;
12935 shellparam.optoff = -1;
12936#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012937 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012938 while (*xargv) {
12939 shellparam.nparam++;
12940 xargv++;
12941 }
12942 optschanged();
12943}
12944
12945/*
12946 * Read /etc/profile or .profile.
12947 */
12948static void
12949read_profile(const char *name)
12950{
12951 int skip;
12952
12953 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12954 return;
12955 skip = cmdloop(0);
12956 popfile();
12957 if (skip)
12958 exitshell();
12959}
12960
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012961/*
12962 * This routine is called when an error or an interrupt occurs in an
12963 * interactive shell and control is returned to the main command loop.
12964 */
12965static void
12966reset(void)
12967{
12968 /* from eval.c: */
12969 evalskip = 0;
12970 loopnest = 0;
12971 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000012972 g_parsefile->left_in_buffer = 0;
12973 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012974 popallfiles();
12975 /* from parser.c: */
12976 tokpushback = 0;
12977 checkkwd = 0;
12978 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000012979 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012980}
12981
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012982#if PROFILE
12983static short profile_buf[16384];
12984extern int etext();
12985#endif
12986
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012987/*
12988 * Main routine. We initialize things, parse the arguments, execute
12989 * profiles if we're a login shell, and then call cmdloop to execute
12990 * commands. The setjmp call sets up the location to jump to when an
12991 * exception occurs. When an exception occurs the variable "state"
12992 * is used to figure out how far we had gotten.
12993 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012994int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012995int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012996{
Mike Frysinger98c52642009-04-02 10:02:37 +000012997 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000012998 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012999 struct jmploc jmploc;
13000 struct stackmark smark;
13001
Denis Vlasenko01631112007-12-16 17:20:38 +000013002 /* Initialize global data */
13003 INIT_G_misc();
13004 INIT_G_memstack();
13005 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013006#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013007 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013008#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013009 INIT_G_cmdtable();
13010
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013011#if PROFILE
13012 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13013#endif
13014
13015#if ENABLE_FEATURE_EDITING
13016 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13017#endif
13018 state = 0;
13019 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013020 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013021 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013022
13023 reset();
13024
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013025 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013026 if (e == EXERROR)
13027 exitstatus = 2;
13028 s = state;
13029 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13030 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013031 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013032 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013033
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013034 popstackmark(&smark);
13035 FORCE_INT_ON; /* enable interrupts */
13036 if (s == 1)
13037 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013038 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013039 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013040 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013041 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013042 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013043 }
13044 exception_handler = &jmploc;
13045#if DEBUG
13046 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013047 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013048 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013049#endif
13050 rootpid = getpid();
13051
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013052 init();
13053 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013054 procargs(argv);
13055
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013056#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13057 if (iflag) {
13058 const char *hp = lookupvar("HISTFILE");
13059
13060 if (hp == NULL) {
13061 hp = lookupvar("HOME");
13062 if (hp != NULL) {
13063 char *defhp = concat_path_file(hp, ".ash_history");
13064 setvar("HISTFILE", defhp, 0);
13065 free(defhp);
13066 }
13067 }
13068 }
13069#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013070 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013071 isloginsh = 1;
13072 if (isloginsh) {
13073 state = 1;
13074 read_profile("/etc/profile");
13075 state1:
13076 state = 2;
13077 read_profile(".profile");
13078 }
13079 state2:
13080 state = 3;
13081 if (
13082#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013083 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013084#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013085 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013086 ) {
13087 shinit = lookupvar("ENV");
13088 if (shinit != NULL && *shinit != '\0') {
13089 read_profile(shinit);
13090 }
13091 }
13092 state3:
13093 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013094 if (minusc) {
13095 /* evalstring pushes parsefile stack.
13096 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013097 * is one of stacked source fds.
13098 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013099 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013100 // ^^ not necessary since now we special-case fd 0
13101 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013102 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013103 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013104
13105 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013106#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013107 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013108 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013109 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013110 line_input_state->hist_file = hp;
13111 }
13112#endif
13113 state4: /* XXX ??? - why isn't this before the "if" statement */
13114 cmdloop(1);
13115 }
13116#if PROFILE
13117 monitor(0);
13118#endif
13119#ifdef GPROF
13120 {
13121 extern void _mcleanup(void);
13122 _mcleanup();
13123 }
13124#endif
13125 exitshell();
13126 /* NOTREACHED */
13127}
13128
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013129
Eric Andersendf82f612001-06-28 07:46:40 +000013130/*-
13131 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013132 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013133 *
13134 * This code is derived from software contributed to Berkeley by
13135 * Kenneth Almquist.
13136 *
13137 * Redistribution and use in source and binary forms, with or without
13138 * modification, are permitted provided that the following conditions
13139 * are met:
13140 * 1. Redistributions of source code must retain the above copyright
13141 * notice, this list of conditions and the following disclaimer.
13142 * 2. Redistributions in binary form must reproduce the above copyright
13143 * notice, this list of conditions and the following disclaimer in the
13144 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013145 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013146 * may be used to endorse or promote products derived from this software
13147 * without specific prior written permission.
13148 *
13149 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13150 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13151 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13152 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13153 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13154 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13155 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13156 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13157 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13158 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13159 * SUCH DAMAGE.
13160 */