blob: 5ef7efbdbf9d2ab88666698852fdfd83cd16d189 [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
Denys Vlasenkod383b492010-09-06 10:22:13 +020077//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko771f1992010-07-16 14:31:34 +020078//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:
Dan Fandrich77d48722010-09-07 23:38:28 -0700956 p++;
957 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000958 break;
959 case CTLVAR:
960 putc('$', fp);
961 putc('{', fp);
962 subtype = *++p;
963 if (subtype == VSLENGTH)
964 putc('#', fp);
965
Dan Fandrich77d48722010-09-07 23:38:28 -0700966 while (*p != '=') {
967 putc(*p, fp);
968 p++;
969 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000970
971 if (subtype & VSNUL)
972 putc(':', fp);
973
974 switch (subtype & VSTYPE) {
975 case VSNORMAL:
976 putc('}', fp);
977 break;
978 case VSMINUS:
979 putc('-', fp);
980 break;
981 case VSPLUS:
982 putc('+', fp);
983 break;
984 case VSQUESTION:
985 putc('?', fp);
986 break;
987 case VSASSIGN:
988 putc('=', fp);
989 break;
990 case VSTRIMLEFT:
991 putc('#', fp);
992 break;
993 case VSTRIMLEFTMAX:
994 putc('#', fp);
995 putc('#', fp);
996 break;
997 case VSTRIMRIGHT:
998 putc('%', fp);
999 break;
1000 case VSTRIMRIGHTMAX:
1001 putc('%', fp);
1002 putc('%', fp);
1003 break;
1004 case VSLENGTH:
1005 break;
1006 default:
1007 out1fmt("<subtype %d>", subtype);
1008 }
1009 break;
1010 case CTLENDVAR:
1011 putc('}', fp);
1012 break;
1013 case CTLBACKQ:
1014 case CTLBACKQ|CTLQUOTE:
1015 putc('$', fp);
1016 putc('(', fp);
1017 shtree(bqlist->n, -1, NULL, fp);
1018 putc(')', fp);
1019 break;
1020 default:
1021 putc(*p, fp);
1022 break;
1023 }
1024 }
1025}
1026
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001027static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001028shcmd(union node *cmd, FILE *fp)
1029{
1030 union node *np;
1031 int first;
1032 const char *s;
1033 int dftfd;
1034
1035 first = 1;
1036 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001037 if (!first)
1038 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001039 sharg(np, fp);
1040 first = 0;
1041 }
1042 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001043 if (!first)
1044 putc(' ', fp);
1045 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001046 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001047 case NTO: s = ">>"+1; dftfd = 1; break;
1048 case NCLOBBER: s = ">|"; dftfd = 1; break;
1049 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001050#if ENABLE_ASH_BASH_COMPAT
1051 case NTO2:
1052#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001053 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001054 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001055 case NFROMFD: s = "<&"; break;
1056 case NFROMTO: s = "<>"; break;
1057 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001058 }
1059 if (np->nfile.fd != dftfd)
1060 fprintf(fp, "%d", np->nfile.fd);
1061 fputs(s, fp);
1062 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1063 fprintf(fp, "%d", np->ndup.dupfd);
1064 } else {
1065 sharg(np->nfile.fname, fp);
1066 }
1067 first = 0;
1068 }
1069}
1070
1071static void
1072shtree(union node *n, int ind, char *pfx, FILE *fp)
1073{
1074 struct nodelist *lp;
1075 const char *s;
1076
1077 if (n == NULL)
1078 return;
1079
1080 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001081
1082 if (n == NODE_EOF) {
1083 fputs("<EOF>", fp);
1084 return;
1085 }
1086
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001087 switch (n->type) {
1088 case NSEMI:
1089 s = "; ";
1090 goto binop;
1091 case NAND:
1092 s = " && ";
1093 goto binop;
1094 case NOR:
1095 s = " || ";
1096 binop:
1097 shtree(n->nbinary.ch1, ind, NULL, fp);
1098 /* if (ind < 0) */
1099 fputs(s, fp);
1100 shtree(n->nbinary.ch2, ind, NULL, fp);
1101 break;
1102 case NCMD:
1103 shcmd(n, fp);
1104 if (ind >= 0)
1105 putc('\n', fp);
1106 break;
1107 case NPIPE:
1108 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001109 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001110 if (lp->next)
1111 fputs(" | ", fp);
1112 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001113 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001114 fputs(" &", fp);
1115 if (ind >= 0)
1116 putc('\n', fp);
1117 break;
1118 default:
1119 fprintf(fp, "<node type %d>", n->type);
1120 if (ind >= 0)
1121 putc('\n', fp);
1122 break;
1123 }
1124}
1125
1126static void
1127showtree(union node *n)
1128{
1129 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001130 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001131}
1132
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001133#endif /* DEBUG */
1134
1135
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001136/* ============ Parser data */
1137
1138/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001139 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1140 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001141struct strlist {
1142 struct strlist *next;
1143 char *text;
1144};
1145
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001146struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001147
Denis Vlasenkob012b102007-02-19 22:43:01 +00001148struct strpush {
1149 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001150 char *prev_string;
1151 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001152#if ENABLE_ASH_ALIAS
1153 struct alias *ap; /* if push was associated with an alias */
1154#endif
1155 char *string; /* remember the string since it may change */
1156};
1157
1158struct parsefile {
1159 struct parsefile *prev; /* preceding file on stack */
1160 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001161 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001162 int left_in_line; /* number of chars left in this line */
1163 int left_in_buffer; /* number of chars left in this buffer past the line */
1164 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001165 char *buf; /* input buffer */
1166 struct strpush *strpush; /* for pushing strings at this level */
1167 struct strpush basestrpush; /* so pushing one is fast */
1168};
1169
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001170static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001171static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001172static int startlinno; /* line # where last token started */
1173static char *commandname; /* currently executing command */
1174static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001175static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001176
1177
1178/* ============ Message printing */
1179
1180static void
1181ash_vmsg(const char *msg, va_list ap)
1182{
1183 fprintf(stderr, "%s: ", arg0);
1184 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001185 if (strcmp(arg0, commandname))
1186 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001187 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001188 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001189 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001190 vfprintf(stderr, msg, ap);
1191 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001192}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001193
1194/*
1195 * Exverror is called to raise the error exception. If the second argument
1196 * is not NULL then error prints an error message using printf style
1197 * formatting. It then raises the error exception.
1198 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001199static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001200static void
1201ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001202{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001203#if DEBUG
1204 if (msg) {
1205 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1206 TRACEV((msg, ap));
1207 TRACE(("\") pid=%d\n", getpid()));
1208 } else
1209 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1210 if (msg)
1211#endif
1212 ash_vmsg(msg, ap);
1213
1214 flush_stdout_stderr();
1215 raise_exception(cond);
1216 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001217}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001218
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001219static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001220static void
1221ash_msg_and_raise_error(const char *msg, ...)
1222{
1223 va_list ap;
1224
1225 va_start(ap, msg);
1226 ash_vmsg_and_raise(EXERROR, msg, ap);
1227 /* NOTREACHED */
1228 va_end(ap);
1229}
1230
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001231static void raise_error_syntax(const char *) NORETURN;
1232static void
1233raise_error_syntax(const char *msg)
1234{
1235 ash_msg_and_raise_error("syntax error: %s", msg);
1236 /* NOTREACHED */
1237}
1238
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001239static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001240static void
1241ash_msg_and_raise(int cond, const char *msg, ...)
1242{
1243 va_list ap;
1244
1245 va_start(ap, msg);
1246 ash_vmsg_and_raise(cond, msg, ap);
1247 /* NOTREACHED */
1248 va_end(ap);
1249}
1250
1251/*
1252 * error/warning routines for external builtins
1253 */
1254static void
1255ash_msg(const char *fmt, ...)
1256{
1257 va_list ap;
1258
1259 va_start(ap, fmt);
1260 ash_vmsg(fmt, ap);
1261 va_end(ap);
1262}
1263
1264/*
1265 * Return a string describing an error. The returned string may be a
1266 * pointer to a static buffer that will be overwritten on the next call.
1267 * Action describes the operation that got the error.
1268 */
1269static const char *
1270errmsg(int e, const char *em)
1271{
1272 if (e == ENOENT || e == ENOTDIR) {
1273 return em;
1274 }
1275 return strerror(e);
1276}
1277
1278
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001279/* ============ Memory allocation */
1280
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001281#if 0
1282/* I consider these wrappers nearly useless:
1283 * ok, they return you to nearest exception handler, but
1284 * how much memory do you leak in the process, making
1285 * memory starvation worse?
1286 */
1287static void *
1288ckrealloc(void * p, size_t nbytes)
1289{
1290 p = realloc(p, nbytes);
1291 if (!p)
1292 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1293 return p;
1294}
1295
1296static void *
1297ckmalloc(size_t nbytes)
1298{
1299 return ckrealloc(NULL, nbytes);
1300}
1301
1302static void *
1303ckzalloc(size_t nbytes)
1304{
1305 return memset(ckmalloc(nbytes), 0, nbytes);
1306}
1307
1308static char *
1309ckstrdup(const char *s)
1310{
1311 char *p = strdup(s);
1312 if (!p)
1313 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1314 return p;
1315}
1316#else
1317/* Using bbox equivalents. They exit if out of memory */
1318# define ckrealloc xrealloc
1319# define ckmalloc xmalloc
1320# define ckzalloc xzalloc
1321# define ckstrdup xstrdup
1322#endif
1323
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001324/*
1325 * It appears that grabstackstr() will barf with such alignments
1326 * because stalloc() will return a string allocated in a new stackblock.
1327 */
1328#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1329enum {
1330 /* Most machines require the value returned from malloc to be aligned
1331 * in some way. The following macro will get this right
1332 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001333 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001334 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001335 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001336};
1337
1338struct stack_block {
1339 struct stack_block *prev;
1340 char space[MINSIZE];
1341};
1342
1343struct stackmark {
1344 struct stack_block *stackp;
1345 char *stacknxt;
1346 size_t stacknleft;
1347 struct stackmark *marknext;
1348};
1349
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001350
Denis Vlasenko01631112007-12-16 17:20:38 +00001351struct globals_memstack {
1352 struct stack_block *g_stackp; // = &stackbase;
1353 struct stackmark *markp;
1354 char *g_stacknxt; // = stackbase.space;
1355 char *sstrend; // = stackbase.space + MINSIZE;
1356 size_t g_stacknleft; // = MINSIZE;
1357 int herefd; // = -1;
1358 struct stack_block stackbase;
1359};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001360extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1361#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001362#define g_stackp (G_memstack.g_stackp )
1363#define markp (G_memstack.markp )
1364#define g_stacknxt (G_memstack.g_stacknxt )
1365#define sstrend (G_memstack.sstrend )
1366#define g_stacknleft (G_memstack.g_stacknleft)
1367#define herefd (G_memstack.herefd )
1368#define stackbase (G_memstack.stackbase )
1369#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001370 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1371 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001372 g_stackp = &stackbase; \
1373 g_stacknxt = stackbase.space; \
1374 g_stacknleft = MINSIZE; \
1375 sstrend = stackbase.space + MINSIZE; \
1376 herefd = -1; \
1377} while (0)
1378
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001379
Denis Vlasenko01631112007-12-16 17:20:38 +00001380#define stackblock() ((void *)g_stacknxt)
1381#define stackblocksize() g_stacknleft
1382
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001383/*
1384 * Parse trees for commands are allocated in lifo order, so we use a stack
1385 * to make this more efficient, and also to avoid all sorts of exception
1386 * handling code to handle interrupts in the middle of a parse.
1387 *
1388 * The size 504 was chosen because the Ultrix malloc handles that size
1389 * well.
1390 */
1391static void *
1392stalloc(size_t nbytes)
1393{
1394 char *p;
1395 size_t aligned;
1396
1397 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001398 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001399 size_t len;
1400 size_t blocksize;
1401 struct stack_block *sp;
1402
1403 blocksize = aligned;
1404 if (blocksize < MINSIZE)
1405 blocksize = MINSIZE;
1406 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1407 if (len < blocksize)
1408 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1409 INT_OFF;
1410 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001411 sp->prev = g_stackp;
1412 g_stacknxt = sp->space;
1413 g_stacknleft = blocksize;
1414 sstrend = g_stacknxt + blocksize;
1415 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001416 INT_ON;
1417 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001418 p = g_stacknxt;
1419 g_stacknxt += aligned;
1420 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001421 return p;
1422}
1423
Denis Vlasenko597906c2008-02-20 16:38:54 +00001424static void *
1425stzalloc(size_t nbytes)
1426{
1427 return memset(stalloc(nbytes), 0, nbytes);
1428}
1429
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001430static void
1431stunalloc(void *p)
1432{
1433#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001434 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001435 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001436 abort();
1437 }
1438#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001439 g_stacknleft += g_stacknxt - (char *)p;
1440 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001441}
1442
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001443/*
1444 * Like strdup but works with the ash stack.
1445 */
1446static char *
1447ststrdup(const char *p)
1448{
1449 size_t len = strlen(p) + 1;
1450 return memcpy(stalloc(len), p, len);
1451}
1452
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001453static void
1454setstackmark(struct stackmark *mark)
1455{
Denis Vlasenko01631112007-12-16 17:20:38 +00001456 mark->stackp = g_stackp;
1457 mark->stacknxt = g_stacknxt;
1458 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001459 mark->marknext = markp;
1460 markp = mark;
1461}
1462
1463static void
1464popstackmark(struct stackmark *mark)
1465{
1466 struct stack_block *sp;
1467
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001468 if (!mark->stackp)
1469 return;
1470
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001471 INT_OFF;
1472 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001473 while (g_stackp != mark->stackp) {
1474 sp = g_stackp;
1475 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001476 free(sp);
1477 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001478 g_stacknxt = mark->stacknxt;
1479 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001480 sstrend = mark->stacknxt + mark->stacknleft;
1481 INT_ON;
1482}
1483
1484/*
1485 * When the parser reads in a string, it wants to stick the string on the
1486 * stack and only adjust the stack pointer when it knows how big the
1487 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1488 * of space on top of the stack and stackblocklen returns the length of
1489 * this block. Growstackblock will grow this space by at least one byte,
1490 * possibly moving it (like realloc). Grabstackblock actually allocates the
1491 * part of the block that has been used.
1492 */
1493static void
1494growstackblock(void)
1495{
1496 size_t newlen;
1497
Denis Vlasenko01631112007-12-16 17:20:38 +00001498 newlen = g_stacknleft * 2;
1499 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001500 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1501 if (newlen < 128)
1502 newlen += 128;
1503
Denis Vlasenko01631112007-12-16 17:20:38 +00001504 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001505 struct stack_block *oldstackp;
1506 struct stackmark *xmark;
1507 struct stack_block *sp;
1508 struct stack_block *prevstackp;
1509 size_t grosslen;
1510
1511 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001512 oldstackp = g_stackp;
1513 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001514 prevstackp = sp->prev;
1515 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1516 sp = ckrealloc(sp, grosslen);
1517 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001518 g_stackp = sp;
1519 g_stacknxt = sp->space;
1520 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001521 sstrend = sp->space + newlen;
1522
1523 /*
1524 * Stack marks pointing to the start of the old block
1525 * must be relocated to point to the new block
1526 */
1527 xmark = markp;
1528 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001529 xmark->stackp = g_stackp;
1530 xmark->stacknxt = g_stacknxt;
1531 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001532 xmark = xmark->marknext;
1533 }
1534 INT_ON;
1535 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001536 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001537 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001538 char *p = stalloc(newlen);
1539
1540 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001541 g_stacknxt = memcpy(p, oldspace, oldlen);
1542 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001543 }
1544}
1545
1546static void
1547grabstackblock(size_t len)
1548{
1549 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001550 g_stacknxt += len;
1551 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001552}
1553
1554/*
1555 * The following routines are somewhat easier to use than the above.
1556 * The user declares a variable of type STACKSTR, which may be declared
1557 * to be a register. The macro STARTSTACKSTR initializes things. Then
1558 * the user uses the macro STPUTC to add characters to the string. In
1559 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1560 * grown as necessary. When the user is done, she can just leave the
1561 * string there and refer to it using stackblock(). Or she can allocate
1562 * the space for it using grabstackstr(). If it is necessary to allow
1563 * someone else to use the stack temporarily and then continue to grow
1564 * the string, the user should use grabstack to allocate the space, and
1565 * then call ungrabstr(p) to return to the previous mode of operation.
1566 *
1567 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1568 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1569 * is space for at least one character.
1570 */
1571static void *
1572growstackstr(void)
1573{
1574 size_t len = stackblocksize();
1575 if (herefd >= 0 && len >= 1024) {
1576 full_write(herefd, stackblock(), len);
1577 return stackblock();
1578 }
1579 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001580 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001581}
1582
1583/*
1584 * Called from CHECKSTRSPACE.
1585 */
1586static char *
1587makestrspace(size_t newlen, char *p)
1588{
Denis Vlasenko01631112007-12-16 17:20:38 +00001589 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001590 size_t size = stackblocksize();
1591
1592 for (;;) {
1593 size_t nleft;
1594
1595 size = stackblocksize();
1596 nleft = size - len;
1597 if (nleft >= newlen)
1598 break;
1599 growstackblock();
1600 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001601 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001602}
1603
1604static char *
1605stack_nputstr(const char *s, size_t n, char *p)
1606{
1607 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001608 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001609 return p;
1610}
1611
1612static char *
1613stack_putstr(const char *s, char *p)
1614{
1615 return stack_nputstr(s, strlen(s), p);
1616}
1617
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001618static char *
1619_STPUTC(int c, char *p)
1620{
1621 if (p == sstrend)
1622 p = growstackstr();
1623 *p++ = c;
1624 return p;
1625}
1626
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001627#define STARTSTACKSTR(p) ((p) = stackblock())
1628#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001629#define CHECKSTRSPACE(n, p) do { \
1630 char *q = (p); \
1631 size_t l = (n); \
1632 size_t m = sstrend - q; \
1633 if (l > m) \
1634 (p) = makestrspace(l, q); \
1635} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001636#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001637#define STACKSTRNUL(p) do { \
1638 if ((p) == sstrend) \
1639 (p) = growstackstr(); \
1640 *(p) = '\0'; \
1641} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001642#define STUNPUTC(p) (--(p))
1643#define STTOPC(p) ((p)[-1])
1644#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001645
1646#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001647#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001648#define stackstrend() ((void *)sstrend)
1649
1650
1651/* ============ String helpers */
1652
1653/*
1654 * prefix -- see if pfx is a prefix of string.
1655 */
1656static char *
1657prefix(const char *string, const char *pfx)
1658{
1659 while (*pfx) {
1660 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001661 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001662 }
1663 return (char *) string;
1664}
1665
1666/*
1667 * Check for a valid number. This should be elsewhere.
1668 */
1669static int
1670is_number(const char *p)
1671{
1672 do {
1673 if (!isdigit(*p))
1674 return 0;
1675 } while (*++p != '\0');
1676 return 1;
1677}
1678
1679/*
1680 * Convert a string of digits to an integer, printing an error message on
1681 * failure.
1682 */
1683static int
1684number(const char *s)
1685{
1686 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001687 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001688 return atoi(s);
1689}
1690
1691/*
1692 * Produce a possibly single quoted string suitable as input to the shell.
1693 * The return string is allocated on the stack.
1694 */
1695static char *
1696single_quote(const char *s)
1697{
1698 char *p;
1699
1700 STARTSTACKSTR(p);
1701
1702 do {
1703 char *q;
1704 size_t len;
1705
1706 len = strchrnul(s, '\'') - s;
1707
1708 q = p = makestrspace(len + 3, p);
1709
1710 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001711 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001712 *q++ = '\'';
1713 s += len;
1714
1715 STADJUST(q - p, p);
1716
Denys Vlasenkocd716832009-11-28 22:14:02 +01001717 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001718 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001719 len = 0;
1720 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001721
1722 q = p = makestrspace(len + 3, p);
1723
1724 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001725 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001727
1728 STADJUST(q - p, p);
1729 } while (*s);
1730
Denys Vlasenkocd716832009-11-28 22:14:02 +01001731 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732
1733 return stackblock();
1734}
1735
1736
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001737/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738
1739static char **argptr; /* argument list for builtin commands */
1740static char *optionarg; /* set by nextopt (like getopt) */
1741static char *optptr; /* used by nextopt */
1742
1743/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001744 * XXX - should get rid of. Have all builtins use getopt(3).
1745 * The library getopt must have the BSD extension static variable
1746 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001747 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001748 * Standard option processing (a la getopt) for builtin routines.
1749 * The only argument that is passed to nextopt is the option string;
1750 * the other arguments are unnecessary. It returns the character,
1751 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001752 */
1753static int
1754nextopt(const char *optstring)
1755{
1756 char *p;
1757 const char *q;
1758 char c;
1759
1760 p = optptr;
1761 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001762 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001763 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001764 if (p == NULL)
1765 return '\0';
1766 if (*p != '-')
1767 return '\0';
1768 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769 return '\0';
1770 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001771 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001773 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001775 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001777 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001778 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001779 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780 if (*++q == ':')
1781 q++;
1782 }
1783 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001784 if (*p == '\0') {
1785 p = *argptr++;
1786 if (p == NULL)
1787 ash_msg_and_raise_error("no arg for -%c option", c);
1788 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001789 optionarg = p;
1790 p = NULL;
1791 }
1792 optptr = p;
1793 return c;
1794}
1795
1796
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001797/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001798
Denis Vlasenko01631112007-12-16 17:20:38 +00001799/*
1800 * The parsefile structure pointed to by the global variable parsefile
1801 * contains information about the current file being read.
1802 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001803struct shparam {
1804 int nparam; /* # of positional parameters (without $0) */
1805#if ENABLE_ASH_GETOPTS
1806 int optind; /* next parameter to be processed by getopts */
1807 int optoff; /* used by getopts */
1808#endif
1809 unsigned char malloced; /* if parameter list dynamically allocated */
1810 char **p; /* parameter list */
1811};
1812
1813/*
1814 * Free the list of positional parameters.
1815 */
1816static void
1817freeparam(volatile struct shparam *param)
1818{
Denis Vlasenko01631112007-12-16 17:20:38 +00001819 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001820 char **ap, **ap1;
1821 ap = ap1 = param->p;
1822 while (*ap)
1823 free(*ap++);
1824 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001825 }
1826}
1827
1828#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001829static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001830#endif
1831
1832struct var {
1833 struct var *next; /* next entry in hash list */
1834 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001835 const char *var_text; /* name=value */
1836 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001837 /* the variable gets set/unset */
1838};
1839
1840struct localvar {
1841 struct localvar *next; /* next local variable in list */
1842 struct var *vp; /* the variable that was made local */
1843 int flags; /* saved flags */
1844 const char *text; /* saved text */
1845};
1846
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001847/* flags */
1848#define VEXPORT 0x01 /* variable is exported */
1849#define VREADONLY 0x02 /* variable cannot be modified */
1850#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1851#define VTEXTFIXED 0x08 /* text is statically allocated */
1852#define VSTACK 0x10 /* text is allocated on the stack */
1853#define VUNSET 0x20 /* the variable is not set */
1854#define VNOFUNC 0x40 /* don't call the callback function */
1855#define VNOSET 0x80 /* do not set variable - just readonly test */
1856#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001857#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001858# define VDYNAMIC 0x200 /* dynamic variable */
1859#else
1860# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001861#endif
1862
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001863
Denis Vlasenko01631112007-12-16 17:20:38 +00001864/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001865#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001866static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001867change_lc_all(const char *value)
1868{
1869 if (value && *value != '\0')
1870 setlocale(LC_ALL, value);
1871}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001872static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001873change_lc_ctype(const char *value)
1874{
1875 if (value && *value != '\0')
1876 setlocale(LC_CTYPE, value);
1877}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001878#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001879#if ENABLE_ASH_MAIL
1880static void chkmail(void);
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001881static void changemail(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001882#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001883static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001884#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001885static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001886#endif
1887
Denis Vlasenko01631112007-12-16 17:20:38 +00001888static const struct {
1889 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001890 const char *var_text;
1891 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001892} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001893 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001894#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001895 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1896 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001897#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001898 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1899 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1900 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1901 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001902#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001903 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001904#endif
1905#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001906 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001907#endif
1908#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001909 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1910 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
1912#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001913 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001914#endif
1915};
1916
Denis Vlasenko0b769642008-07-24 07:54:57 +00001917struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001918
1919struct globals_var {
1920 struct shparam shellparam; /* $@ current positional parameters */
1921 struct redirtab *redirlist;
1922 int g_nullredirs;
1923 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1924 struct var *vartab[VTABSIZE];
1925 struct var varinit[ARRAY_SIZE(varinit_data)];
1926};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001927extern struct globals_var *const ash_ptr_to_globals_var;
1928#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001929#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001930//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001931#define g_nullredirs (G_var.g_nullredirs )
1932#define preverrout_fd (G_var.preverrout_fd)
1933#define vartab (G_var.vartab )
1934#define varinit (G_var.varinit )
1935#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001936 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001937 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1938 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001939 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001940 varinit[i].flags = varinit_data[i].flags; \
1941 varinit[i].var_text = varinit_data[i].var_text; \
1942 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001943 } \
1944} while (0)
1945
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001946#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001947#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001948# define vmail (&vifs)[1]
1949# define vmpath (&vmail)[1]
1950# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001951#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001952# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001953#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001954#define vps1 (&vpath)[1]
1955#define vps2 (&vps1)[1]
1956#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001957#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001958# define voptind (&vps4)[1]
1959# if ENABLE_ASH_RANDOM_SUPPORT
1960# define vrandom (&voptind)[1]
1961# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001962#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001963# if ENABLE_ASH_RANDOM_SUPPORT
1964# define vrandom (&vps4)[1]
1965# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001966#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001967
1968/*
1969 * The following macros access the values of the above variables.
1970 * They have to skip over the name. They return the null string
1971 * for unset variables.
1972 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001973#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001974#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001975#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001976# define mailval() (vmail.var_text + 5)
1977# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001978# define mpathset() ((vmpath.flags & VUNSET) == 0)
1979#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001980#define pathval() (vpath.var_text + 5)
1981#define ps1val() (vps1.var_text + 4)
1982#define ps2val() (vps2.var_text + 4)
1983#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001984#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001985# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001986#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001987
Denis Vlasenko01631112007-12-16 17:20:38 +00001988#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001989static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001990getoptsreset(const char *value)
1991{
1992 shellparam.optind = number(value);
1993 shellparam.optoff = -1;
1994}
1995#endif
1996
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02001997/* math.h has these, otherwise define our private copies */
1998#if !ENABLE_SH_MATH_SUPPORT
1999#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2000#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002001/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002002 * Return the pointer to the first char which is not part of a legal variable name
2003 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002004 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002005static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002006endofname(const char *name)
2007{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002008 if (!is_name(*name))
2009 return name;
2010 while (*++name) {
2011 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002012 break;
2013 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002014 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002015}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002016#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002017
2018/*
2019 * Compares two strings up to the first = or '\0'. The first
2020 * string must be terminated by '='; the second may be terminated by
2021 * either '=' or '\0'.
2022 */
2023static int
2024varcmp(const char *p, const char *q)
2025{
2026 int c, d;
2027
2028 while ((c = *p) == (d = *q)) {
2029 if (!c || c == '=')
2030 goto out;
2031 p++;
2032 q++;
2033 }
2034 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002035 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002036 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002037 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002038 out:
2039 return c - d;
2040}
2041
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002042/*
2043 * Find the appropriate entry in the hash table from the name.
2044 */
2045static struct var **
2046hashvar(const char *p)
2047{
2048 unsigned hashval;
2049
2050 hashval = ((unsigned char) *p) << 4;
2051 while (*p && *p != '=')
2052 hashval += (unsigned char) *p++;
2053 return &vartab[hashval % VTABSIZE];
2054}
2055
2056static int
2057vpcmp(const void *a, const void *b)
2058{
2059 return varcmp(*(const char **)a, *(const char **)b);
2060}
2061
2062/*
2063 * This routine initializes the builtin variables.
2064 */
2065static void
2066initvar(void)
2067{
2068 struct var *vp;
2069 struct var *end;
2070 struct var **vpp;
2071
2072 /*
2073 * PS1 depends on uid
2074 */
2075#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002076 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002077#else
2078 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002079 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002080#endif
2081 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002082 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002083 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002084 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002085 vp->next = *vpp;
2086 *vpp = vp;
2087 } while (++vp < end);
2088}
2089
2090static struct var **
2091findvar(struct var **vpp, const char *name)
2092{
2093 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002094 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002095 break;
2096 }
2097 }
2098 return vpp;
2099}
2100
2101/*
2102 * Find the value of a variable. Returns NULL if not set.
2103 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002104static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002105lookupvar(const char *name)
2106{
2107 struct var *v;
2108
2109 v = *findvar(hashvar(name), name);
2110 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002111#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112 /*
2113 * Dynamic variables are implemented roughly the same way they are
2114 * in bash. Namely, they're "special" so long as they aren't unset.
2115 * As soon as they're unset, they're no longer dynamic, and dynamic
2116 * lookup will no longer happen at that point. -- PFM.
2117 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002118 if (v->flags & VDYNAMIC)
2119 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002120#endif
2121 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002122 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002123 }
2124 return NULL;
2125}
2126
2127/*
2128 * Search the environment of a builtin command.
2129 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002130static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002131bltinlookup(const char *name)
2132{
2133 struct strlist *sp;
2134
2135 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002136 if (varcmp(sp->text, name) == 0)
2137 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002138 }
2139 return lookupvar(name);
2140}
2141
2142/*
2143 * Same as setvar except that the variable and value are passed in
2144 * the first argument as name=value. Since the first argument will
2145 * be actually stored in the table, it should not be a string that
2146 * will go away.
2147 * Called with interrupts off.
2148 */
2149static void
2150setvareq(char *s, int flags)
2151{
2152 struct var *vp, **vpp;
2153
2154 vpp = hashvar(s);
2155 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2156 vp = *findvar(vpp, s);
2157 if (vp) {
2158 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2159 const char *n;
2160
2161 if (flags & VNOSAVE)
2162 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002163 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002164 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2165 }
2166
2167 if (flags & VNOSET)
2168 return;
2169
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002170 if (vp->var_func && !(flags & VNOFUNC))
2171 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002172
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002173 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2174 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002175
2176 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2177 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002178 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002179 if (flags & VNOSET)
2180 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002181 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002182 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002183 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002184 *vpp = vp;
2185 }
2186 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2187 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002188 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002189 vp->flags = flags;
2190}
2191
2192/*
2193 * Set the value of a variable. The flags argument is ored with the
2194 * flags of the variable. If val is NULL, the variable is unset.
2195 */
2196static void
2197setvar(const char *name, const char *val, int flags)
2198{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002199 const char *q;
2200 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002201 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002202 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002203 size_t vallen;
2204
2205 q = endofname(name);
2206 p = strchrnul(q, '=');
2207 namelen = p - name;
2208 if (!namelen || p != q)
2209 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2210 vallen = 0;
2211 if (val == NULL) {
2212 flags |= VUNSET;
2213 } else {
2214 vallen = strlen(val);
2215 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002216
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002217 INT_OFF;
2218 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002219 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002220 if (val) {
2221 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002222 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002223 }
2224 *p = '\0';
2225 setvareq(nameeq, flags | VNOSAVE);
2226 INT_ON;
2227}
2228
Denys Vlasenko03dad222010-01-12 23:29:57 +01002229static void FAST_FUNC
2230setvar2(const char *name, const char *val)
2231{
2232 setvar(name, val, 0);
2233}
2234
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002235#if ENABLE_ASH_GETOPTS
2236/*
2237 * Safe version of setvar, returns 1 on success 0 on failure.
2238 */
2239static int
2240setvarsafe(const char *name, const char *val, int flags)
2241{
2242 int err;
2243 volatile int saveint;
2244 struct jmploc *volatile savehandler = exception_handler;
2245 struct jmploc jmploc;
2246
2247 SAVE_INT(saveint);
2248 if (setjmp(jmploc.loc))
2249 err = 1;
2250 else {
2251 exception_handler = &jmploc;
2252 setvar(name, val, flags);
2253 err = 0;
2254 }
2255 exception_handler = savehandler;
2256 RESTORE_INT(saveint);
2257 return err;
2258}
2259#endif
2260
2261/*
2262 * Unset the specified variable.
2263 */
2264static int
2265unsetvar(const char *s)
2266{
2267 struct var **vpp;
2268 struct var *vp;
2269 int retval;
2270
2271 vpp = findvar(hashvar(s), s);
2272 vp = *vpp;
2273 retval = 2;
2274 if (vp) {
2275 int flags = vp->flags;
2276
2277 retval = 1;
2278 if (flags & VREADONLY)
2279 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002280#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002281 vp->flags &= ~VDYNAMIC;
2282#endif
2283 if (flags & VUNSET)
2284 goto ok;
2285 if ((flags & VSTRFIXED) == 0) {
2286 INT_OFF;
2287 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002288 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002289 *vpp = vp->next;
2290 free(vp);
2291 INT_ON;
2292 } else {
2293 setvar(s, 0, 0);
2294 vp->flags &= ~VEXPORT;
2295 }
2296 ok:
2297 retval = 0;
2298 }
2299 out:
2300 return retval;
2301}
2302
2303/*
2304 * Process a linked list of variable assignments.
2305 */
2306static void
2307listsetvar(struct strlist *list_set_var, int flags)
2308{
2309 struct strlist *lp = list_set_var;
2310
2311 if (!lp)
2312 return;
2313 INT_OFF;
2314 do {
2315 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002316 lp = lp->next;
2317 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002318 INT_ON;
2319}
2320
2321/*
2322 * Generate a list of variables satisfying the given conditions.
2323 */
2324static char **
2325listvars(int on, int off, char ***end)
2326{
2327 struct var **vpp;
2328 struct var *vp;
2329 char **ep;
2330 int mask;
2331
2332 STARTSTACKSTR(ep);
2333 vpp = vartab;
2334 mask = on | off;
2335 do {
2336 for (vp = *vpp; vp; vp = vp->next) {
2337 if ((vp->flags & mask) == on) {
2338 if (ep == stackstrend())
2339 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002340 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002341 }
2342 }
2343 } while (++vpp < vartab + VTABSIZE);
2344 if (ep == stackstrend())
2345 ep = growstackstr();
2346 if (end)
2347 *end = ep;
2348 *ep++ = NULL;
2349 return grabstackstr(ep);
2350}
2351
2352
2353/* ============ Path search helper
2354 *
2355 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002356 * of the path before the first call; path_advance will update
2357 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002358 * the possible path expansions in sequence. If an option (indicated by
2359 * a percent sign) appears in the path entry then the global variable
2360 * pathopt will be set to point to it; otherwise pathopt will be set to
2361 * NULL.
2362 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002363static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002364
2365static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002366path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002367{
2368 const char *p;
2369 char *q;
2370 const char *start;
2371 size_t len;
2372
2373 if (*path == NULL)
2374 return NULL;
2375 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002376 for (p = start; *p && *p != ':' && *p != '%'; p++)
2377 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002378 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2379 while (stackblocksize() < len)
2380 growstackblock();
2381 q = stackblock();
2382 if (p != start) {
2383 memcpy(q, start, p - start);
2384 q += p - start;
2385 *q++ = '/';
2386 }
2387 strcpy(q, name);
2388 pathopt = NULL;
2389 if (*p == '%') {
2390 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002391 while (*p && *p != ':')
2392 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002393 }
2394 if (*p == ':')
2395 *path = p + 1;
2396 else
2397 *path = NULL;
2398 return stalloc(len);
2399}
2400
2401
2402/* ============ Prompt */
2403
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002404static smallint doprompt; /* if set, prompt the user */
2405static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002406
2407#if ENABLE_FEATURE_EDITING
2408static line_input_t *line_input_state;
2409static const char *cmdedit_prompt;
2410static void
2411putprompt(const char *s)
2412{
2413 if (ENABLE_ASH_EXPAND_PRMT) {
2414 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002415 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002416 return;
2417 }
2418 cmdedit_prompt = s;
2419}
2420#else
2421static void
2422putprompt(const char *s)
2423{
2424 out2str(s);
2425}
2426#endif
2427
2428#if ENABLE_ASH_EXPAND_PRMT
2429/* expandstr() needs parsing machinery, so it is far away ahead... */
2430static const char *expandstr(const char *ps);
2431#else
2432#define expandstr(s) s
2433#endif
2434
2435static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002436setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002437{
2438 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002439 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2440
2441 if (!do_set)
2442 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002443
2444 needprompt = 0;
2445
2446 switch (whichprompt) {
2447 case 1:
2448 prompt = ps1val();
2449 break;
2450 case 2:
2451 prompt = ps2val();
2452 break;
2453 default: /* 0 */
2454 prompt = nullstr;
2455 }
2456#if ENABLE_ASH_EXPAND_PRMT
2457 setstackmark(&smark);
2458 stalloc(stackblocksize());
2459#endif
2460 putprompt(expandstr(prompt));
2461#if ENABLE_ASH_EXPAND_PRMT
2462 popstackmark(&smark);
2463#endif
2464}
2465
2466
2467/* ============ The cd and pwd commands */
2468
2469#define CD_PHYSICAL 1
2470#define CD_PRINT 2
2471
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002472static int
2473cdopt(void)
2474{
2475 int flags = 0;
2476 int i, j;
2477
2478 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002479 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002480 if (i != j) {
2481 flags ^= CD_PHYSICAL;
2482 j = i;
2483 }
2484 }
2485
2486 return flags;
2487}
2488
2489/*
2490 * Update curdir (the name of the current directory) in response to a
2491 * cd command.
2492 */
2493static const char *
2494updatepwd(const char *dir)
2495{
2496 char *new;
2497 char *p;
2498 char *cdcomppath;
2499 const char *lim;
2500
2501 cdcomppath = ststrdup(dir);
2502 STARTSTACKSTR(new);
2503 if (*dir != '/') {
2504 if (curdir == nullstr)
2505 return 0;
2506 new = stack_putstr(curdir, new);
2507 }
2508 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002509 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002510 if (*dir != '/') {
2511 if (new[-1] != '/')
2512 USTPUTC('/', new);
2513 if (new > lim && *lim == '/')
2514 lim++;
2515 } else {
2516 USTPUTC('/', new);
2517 cdcomppath++;
2518 if (dir[1] == '/' && dir[2] != '/') {
2519 USTPUTC('/', new);
2520 cdcomppath++;
2521 lim++;
2522 }
2523 }
2524 p = strtok(cdcomppath, "/");
2525 while (p) {
2526 switch (*p) {
2527 case '.':
2528 if (p[1] == '.' && p[2] == '\0') {
2529 while (new > lim) {
2530 STUNPUTC(new);
2531 if (new[-1] == '/')
2532 break;
2533 }
2534 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002535 }
2536 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002537 break;
2538 /* fall through */
2539 default:
2540 new = stack_putstr(p, new);
2541 USTPUTC('/', new);
2542 }
2543 p = strtok(0, "/");
2544 }
2545 if (new > lim)
2546 STUNPUTC(new);
2547 *new = 0;
2548 return stackblock();
2549}
2550
2551/*
2552 * Find out what the current directory is. If we already know the current
2553 * directory, this routine returns immediately.
2554 */
2555static char *
2556getpwd(void)
2557{
Denis Vlasenko01631112007-12-16 17:20:38 +00002558 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002559 return dir ? dir : nullstr;
2560}
2561
2562static void
2563setpwd(const char *val, int setold)
2564{
2565 char *oldcur, *dir;
2566
2567 oldcur = dir = curdir;
2568
2569 if (setold) {
2570 setvar("OLDPWD", oldcur, VEXPORT);
2571 }
2572 INT_OFF;
2573 if (physdir != nullstr) {
2574 if (physdir != oldcur)
2575 free(physdir);
2576 physdir = nullstr;
2577 }
2578 if (oldcur == val || !val) {
2579 char *s = getpwd();
2580 physdir = s;
2581 if (!val)
2582 dir = s;
2583 } else
2584 dir = ckstrdup(val);
2585 if (oldcur != dir && oldcur != nullstr) {
2586 free(oldcur);
2587 }
2588 curdir = dir;
2589 INT_ON;
2590 setvar("PWD", dir, VEXPORT);
2591}
2592
2593static void hashcd(void);
2594
2595/*
2596 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2597 * know that the current directory has changed.
2598 */
2599static int
2600docd(const char *dest, int flags)
2601{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002602 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002603 int err;
2604
2605 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2606
2607 INT_OFF;
2608 if (!(flags & CD_PHYSICAL)) {
2609 dir = updatepwd(dest);
2610 if (dir)
2611 dest = dir;
2612 }
2613 err = chdir(dest);
2614 if (err)
2615 goto out;
2616 setpwd(dir, 1);
2617 hashcd();
2618 out:
2619 INT_ON;
2620 return err;
2621}
2622
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002623static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002624cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002625{
2626 const char *dest;
2627 const char *path;
2628 const char *p;
2629 char c;
2630 struct stat statb;
2631 int flags;
2632
2633 flags = cdopt();
2634 dest = *argptr;
2635 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002636 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002637 else if (LONE_DASH(dest)) {
2638 dest = bltinlookup("OLDPWD");
2639 flags |= CD_PRINT;
2640 }
2641 if (!dest)
2642 dest = nullstr;
2643 if (*dest == '/')
2644 goto step7;
2645 if (*dest == '.') {
2646 c = dest[1];
2647 dotdot:
2648 switch (c) {
2649 case '\0':
2650 case '/':
2651 goto step6;
2652 case '.':
2653 c = dest[2];
2654 if (c != '.')
2655 goto dotdot;
2656 }
2657 }
2658 if (!*dest)
2659 dest = ".";
2660 path = bltinlookup("CDPATH");
2661 if (!path) {
2662 step6:
2663 step7:
2664 p = dest;
2665 goto docd;
2666 }
2667 do {
2668 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002669 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002670 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2671 if (c && c != ':')
2672 flags |= CD_PRINT;
2673 docd:
2674 if (!docd(p, flags))
2675 goto out;
2676 break;
2677 }
2678 } while (path);
2679 ash_msg_and_raise_error("can't cd to %s", dest);
2680 /* NOTREACHED */
2681 out:
2682 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002683 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002684 return 0;
2685}
2686
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002687static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002688pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002689{
2690 int flags;
2691 const char *dir = curdir;
2692
2693 flags = cdopt();
2694 if (flags) {
2695 if (physdir == nullstr)
2696 setpwd(dir, 0);
2697 dir = physdir;
2698 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002699 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002700 return 0;
2701}
2702
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002703
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002704/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002705
Denis Vlasenko834dee72008-10-07 09:18:30 +00002706
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002707#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002708
Eric Andersenc470f442003-07-28 09:56:35 +00002709/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002710#define CWORD 0 /* character is nothing special */
2711#define CNL 1 /* newline character */
2712#define CBACK 2 /* a backslash character */
2713#define CSQUOTE 3 /* single quote */
2714#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002715#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002716#define CBQUOTE 6 /* backwards single quote */
2717#define CVAR 7 /* a dollar sign */
2718#define CENDVAR 8 /* a '}' character */
2719#define CLP 9 /* a left paren in arithmetic */
2720#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002721#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002722#define CCTL 12 /* like CWORD, except it must be escaped */
2723#define CSPCL 13 /* these terminate a word */
2724#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002725
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002726#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002727#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002728# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002729#endif
2730
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002731#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002732
Mike Frysinger98c52642009-04-02 10:02:37 +00002733#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002734# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002735#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002736# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002737#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002738static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002739#if ENABLE_ASH_ALIAS
2740 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2741#endif
2742 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2743 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2744 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2745 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2746 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2747 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2748 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2749 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2750 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2751 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2752 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002753#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002754 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2755 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2756 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2757#endif
2758#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002759};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002760/* Constants below must match table above */
2761enum {
2762#if ENABLE_ASH_ALIAS
2763 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2764#endif
2765 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2766 CNL_CNL_CNL_CNL , /* 2 */
2767 CWORD_CCTL_CCTL_CWORD , /* 3 */
2768 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2769 CVAR_CVAR_CWORD_CVAR , /* 5 */
2770 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2771 CSPCL_CWORD_CWORD_CLP , /* 7 */
2772 CSPCL_CWORD_CWORD_CRP , /* 8 */
2773 CBACK_CBACK_CCTL_CBACK , /* 9 */
2774 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2775 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2776 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2777 CWORD_CWORD_CWORD_CWORD , /* 13 */
2778 CCTL_CCTL_CCTL_CCTL , /* 14 */
2779};
Eric Andersen2870d962001-07-02 17:27:21 +00002780
Denys Vlasenkocd716832009-11-28 22:14:02 +01002781/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2782 * caller must ensure proper cast on it if c is *char_ptr!
2783 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002784/* Values for syntax param */
2785#define BASESYNTAX 0 /* not in quotes */
2786#define DQSYNTAX 1 /* in double quotes */
2787#define SQSYNTAX 2 /* in single quotes */
2788#define ARISYNTAX 3 /* in arithmetic */
2789#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002790
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002791#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002792
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002793static int
2794SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002795{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002796 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002797# if ENABLE_ASH_ALIAS
2798 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002799 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2800 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2801 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2802 11, 3 /* "}~" */
2803 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002804# else
2805 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002806 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2807 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2808 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2809 10, 2 /* "}~" */
2810 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002811# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002812 const char *s;
2813 int indx;
2814
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002815 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002816 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002817# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002818 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002819 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002820 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002821# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002822 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002823 /* Cast is purely for paranoia here,
2824 * just in case someone passed signed char to us */
2825 if ((unsigned char)c >= CTL_FIRST
2826 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002827 ) {
2828 return CCTL;
2829 }
2830 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002831 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002832 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002833 indx = syntax_index_table[s - spec_symbls];
2834 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002835 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002836}
2837
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002838#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002839
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002840static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002841 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002842 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2852 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2853 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2875 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2876 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2877 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2879 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2881 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2882 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2883 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2884 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2885 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2887 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2888 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2889 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2901 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2902 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2903 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2904 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2905 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2934 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2935 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2939 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2967 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2968 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2969 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2970 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2971 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2972 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2973 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2974 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2975 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2976 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2977 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2978 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2979 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2980 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2981 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2982 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2983 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2986 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 150 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 151 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 152 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 153 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 154 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 155 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 156 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003098 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003099# if ENABLE_ASH_ALIAS
3100 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3101# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003102};
3103
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003104# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003105
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003106#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003107
Eric Andersen2870d962001-07-02 17:27:21 +00003108
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003109/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003110
Denis Vlasenko131ae172007-02-18 13:00:19 +00003111#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003112
3113#define ALIASINUSE 1
3114#define ALIASDEAD 2
3115
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003116struct alias {
3117 struct alias *next;
3118 char *name;
3119 char *val;
3120 int flag;
3121};
3122
Denis Vlasenko01631112007-12-16 17:20:38 +00003123
3124static struct alias **atab; // [ATABSIZE];
3125#define INIT_G_alias() do { \
3126 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3127} while (0)
3128
Eric Andersen2870d962001-07-02 17:27:21 +00003129
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003130static struct alias **
3131__lookupalias(const char *name) {
3132 unsigned int hashval;
3133 struct alias **app;
3134 const char *p;
3135 unsigned int ch;
3136
3137 p = name;
3138
3139 ch = (unsigned char)*p;
3140 hashval = ch << 4;
3141 while (ch) {
3142 hashval += ch;
3143 ch = (unsigned char)*++p;
3144 }
3145 app = &atab[hashval % ATABSIZE];
3146
3147 for (; *app; app = &(*app)->next) {
3148 if (strcmp(name, (*app)->name) == 0) {
3149 break;
3150 }
3151 }
3152
3153 return app;
3154}
3155
3156static struct alias *
3157lookupalias(const char *name, int check)
3158{
3159 struct alias *ap = *__lookupalias(name);
3160
3161 if (check && ap && (ap->flag & ALIASINUSE))
3162 return NULL;
3163 return ap;
3164}
3165
3166static struct alias *
3167freealias(struct alias *ap)
3168{
3169 struct alias *next;
3170
3171 if (ap->flag & ALIASINUSE) {
3172 ap->flag |= ALIASDEAD;
3173 return ap;
3174 }
3175
3176 next = ap->next;
3177 free(ap->name);
3178 free(ap->val);
3179 free(ap);
3180 return next;
3181}
Eric Andersencb57d552001-06-28 07:25:16 +00003182
Eric Andersenc470f442003-07-28 09:56:35 +00003183static void
3184setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003185{
3186 struct alias *ap, **app;
3187
3188 app = __lookupalias(name);
3189 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003190 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003191 if (ap) {
3192 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003193 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003194 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003195 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003196 ap->flag &= ~ALIASDEAD;
3197 } else {
3198 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003199 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003200 ap->name = ckstrdup(name);
3201 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003202 /*ap->flag = 0; - ckzalloc did it */
3203 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003204 *app = ap;
3205 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003206 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003207}
3208
Eric Andersenc470f442003-07-28 09:56:35 +00003209static int
3210unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003211{
Eric Andersencb57d552001-06-28 07:25:16 +00003212 struct alias **app;
3213
3214 app = __lookupalias(name);
3215
3216 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003217 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003218 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003219 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003220 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003221 }
3222
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003223 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003224}
3225
Eric Andersenc470f442003-07-28 09:56:35 +00003226static void
3227rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003228{
Eric Andersencb57d552001-06-28 07:25:16 +00003229 struct alias *ap, **app;
3230 int i;
3231
Denis Vlasenkob012b102007-02-19 22:43:01 +00003232 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003233 for (i = 0; i < ATABSIZE; i++) {
3234 app = &atab[i];
3235 for (ap = *app; ap; ap = *app) {
3236 *app = freealias(*app);
3237 if (ap == *app) {
3238 app = &ap->next;
3239 }
3240 }
3241 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003242 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003243}
3244
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003245static void
3246printalias(const struct alias *ap)
3247{
3248 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3249}
3250
Eric Andersencb57d552001-06-28 07:25:16 +00003251/*
3252 * TODO - sort output
3253 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003254static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003255aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003256{
3257 char *n, *v;
3258 int ret = 0;
3259 struct alias *ap;
3260
Denis Vlasenko68404f12008-03-17 09:00:54 +00003261 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003262 int i;
3263
Denis Vlasenko68404f12008-03-17 09:00:54 +00003264 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003265 for (ap = atab[i]; ap; ap = ap->next) {
3266 printalias(ap);
3267 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003268 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003269 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003270 }
3271 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003272 v = strchr(n+1, '=');
3273 if (v == NULL) { /* n+1: funny ksh stuff */
3274 ap = *__lookupalias(n);
3275 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003276 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003277 ret = 1;
3278 } else
3279 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003280 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003281 *v++ = '\0';
3282 setalias(n, v);
3283 }
3284 }
3285
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003286 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003287}
3288
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003289static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003290unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003291{
3292 int i;
3293
3294 while ((i = nextopt("a")) != '\0') {
3295 if (i == 'a') {
3296 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003297 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003298 }
3299 }
3300 for (i = 0; *argptr; argptr++) {
3301 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003302 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003303 i = 1;
3304 }
3305 }
3306
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003307 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003308}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003309
Denis Vlasenko131ae172007-02-18 13:00:19 +00003310#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003311
Eric Andersenc470f442003-07-28 09:56:35 +00003312
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003313/* ============ jobs.c */
3314
3315/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003316#define FORK_FG 0
3317#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003318#define FORK_NOJOB 2
3319
3320/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003321#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3322#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3323#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003324
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003325/*
3326 * A job structure contains information about a job. A job is either a
3327 * single process or a set of processes contained in a pipeline. In the
3328 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3329 * array of pids.
3330 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003332 pid_t ps_pid; /* process id */
3333 int ps_status; /* last process status from wait() */
3334 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003335};
3336
3337struct job {
3338 struct procstat ps0; /* status of process */
3339 struct procstat *ps; /* status or processes when more than one */
3340#if JOBS
3341 int stopstatus; /* status of a stopped job */
3342#endif
3343 uint32_t
3344 nprocs: 16, /* number of processes */
3345 state: 8,
3346#define JOBRUNNING 0 /* at least one proc running */
3347#define JOBSTOPPED 1 /* all procs are stopped */
3348#define JOBDONE 2 /* all procs are completed */
3349#if JOBS
3350 sigint: 1, /* job was killed by SIGINT */
3351 jobctl: 1, /* job running under job control */
3352#endif
3353 waited: 1, /* true if this entry has been waited for */
3354 used: 1, /* true if this entry is in used */
3355 changed: 1; /* true if status has changed */
3356 struct job *prev_job; /* previous job */
3357};
3358
Denis Vlasenko68404f12008-03-17 09:00:54 +00003359static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003360static int forkshell(struct job *, union node *, int);
3361static int waitforjob(struct job *);
3362
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003363#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003364enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003365#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003366#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003367static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003368static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003369#endif
3370
3371/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003372 * Ignore a signal.
3373 */
3374static void
3375ignoresig(int signo)
3376{
3377 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3378 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3379 /* No, need to do it */
3380 signal(signo, SIG_IGN);
3381 }
3382 sigmode[signo - 1] = S_HARD_IGN;
3383}
3384
3385/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003386 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003387 */
3388static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003389signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003390{
3391 gotsig[signo - 1] = 1;
3392
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003393 if (signo == SIGINT && !trap[SIGINT]) {
3394 if (!suppress_int) {
3395 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003396 raise_interrupt(); /* does not return */
3397 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003398 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003399 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003400 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003401 }
3402}
3403
3404/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003405 * Set the signal handler for the specified signal. The routine figures
3406 * out what it should be set to.
3407 */
3408static void
3409setsignal(int signo)
3410{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003411 char *t;
3412 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003413 struct sigaction act;
3414
3415 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003416 new_act = S_DFL;
3417 if (t != NULL) { /* trap for this sig is set */
3418 new_act = S_CATCH;
3419 if (t[0] == '\0') /* trap is "": ignore this sig */
3420 new_act = S_IGN;
3421 }
3422
3423 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003424 switch (signo) {
3425 case SIGINT:
3426 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003427 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003428 break;
3429 case SIGQUIT:
3430#if DEBUG
3431 if (debug)
3432 break;
3433#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003434 /* man bash:
3435 * "In all cases, bash ignores SIGQUIT. Non-builtin
3436 * commands run by bash have signal handlers
3437 * set to the values inherited by the shell
3438 * from its parent". */
3439 new_act = S_IGN;
3440 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003441 case SIGTERM:
3442 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003443 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003444 break;
3445#if JOBS
3446 case SIGTSTP:
3447 case SIGTTOU:
3448 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003449 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003450 break;
3451#endif
3452 }
3453 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003454//TODO: if !rootshell, we reset SIGQUIT to DFL,
3455//whereas we have to restore it to what shell got on entry
3456//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003457
3458 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003459 cur_act = *t;
3460 if (cur_act == 0) {
3461 /* current setting is not yet known */
3462 if (sigaction(signo, NULL, &act)) {
3463 /* pretend it worked; maybe we should give a warning,
3464 * but other shells don't. We don't alter sigmode,
3465 * so we retry every time.
3466 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003467 return;
3468 }
3469 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003470 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003471 if (mflag
3472 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3473 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003474 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003475 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003476 }
3477 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003478 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003479 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003480
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003481 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003482 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003483 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003484 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003485 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3486 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003487 break;
3488 case S_IGN:
3489 act.sa_handler = SIG_IGN;
3490 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003491 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003492 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003493
3494 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003495}
3496
3497/* mode flags for set_curjob */
3498#define CUR_DELETE 2
3499#define CUR_RUNNING 1
3500#define CUR_STOPPED 0
3501
3502/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003503#define DOWAIT_NONBLOCK WNOHANG
3504#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003505
3506#if JOBS
3507/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003508static int initialpgrp; //references:2
3509static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003510#endif
3511/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003512static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003513/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003514static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003515/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003516static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003517/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003518static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003519
3520static void
3521set_curjob(struct job *jp, unsigned mode)
3522{
3523 struct job *jp1;
3524 struct job **jpp, **curp;
3525
3526 /* first remove from list */
3527 jpp = curp = &curjob;
3528 do {
3529 jp1 = *jpp;
3530 if (jp1 == jp)
3531 break;
3532 jpp = &jp1->prev_job;
3533 } while (1);
3534 *jpp = jp1->prev_job;
3535
3536 /* Then re-insert in correct position */
3537 jpp = curp;
3538 switch (mode) {
3539 default:
3540#if DEBUG
3541 abort();
3542#endif
3543 case CUR_DELETE:
3544 /* job being deleted */
3545 break;
3546 case CUR_RUNNING:
3547 /* newly created job or backgrounded job,
3548 put after all stopped jobs. */
3549 do {
3550 jp1 = *jpp;
3551#if JOBS
3552 if (!jp1 || jp1->state != JOBSTOPPED)
3553#endif
3554 break;
3555 jpp = &jp1->prev_job;
3556 } while (1);
3557 /* FALLTHROUGH */
3558#if JOBS
3559 case CUR_STOPPED:
3560#endif
3561 /* newly stopped job - becomes curjob */
3562 jp->prev_job = *jpp;
3563 *jpp = jp;
3564 break;
3565 }
3566}
3567
3568#if JOBS || DEBUG
3569static int
3570jobno(const struct job *jp)
3571{
3572 return jp - jobtab + 1;
3573}
3574#endif
3575
3576/*
3577 * Convert a job name to a job structure.
3578 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003579#if !JOBS
3580#define getjob(name, getctl) getjob(name)
3581#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003582static struct job *
3583getjob(const char *name, int getctl)
3584{
3585 struct job *jp;
3586 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003587 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003588 unsigned num;
3589 int c;
3590 const char *p;
3591 char *(*match)(const char *, const char *);
3592
3593 jp = curjob;
3594 p = name;
3595 if (!p)
3596 goto currentjob;
3597
3598 if (*p != '%')
3599 goto err;
3600
3601 c = *++p;
3602 if (!c)
3603 goto currentjob;
3604
3605 if (!p[1]) {
3606 if (c == '+' || c == '%') {
3607 currentjob:
3608 err_msg = "No current job";
3609 goto check;
3610 }
3611 if (c == '-') {
3612 if (jp)
3613 jp = jp->prev_job;
3614 err_msg = "No previous job";
3615 check:
3616 if (!jp)
3617 goto err;
3618 goto gotit;
3619 }
3620 }
3621
3622 if (is_number(p)) {
3623 num = atoi(p);
3624 if (num < njobs) {
3625 jp = jobtab + num - 1;
3626 if (jp->used)
3627 goto gotit;
3628 goto err;
3629 }
3630 }
3631
3632 match = prefix;
3633 if (*p == '?') {
3634 match = strstr;
3635 p++;
3636 }
3637
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003638 found = NULL;
3639 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003640 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003641 if (found)
3642 goto err;
3643 found = jp;
3644 err_msg = "%s: ambiguous";
3645 }
3646 jp = jp->prev_job;
3647 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003648 if (!found)
3649 goto err;
3650 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003651
3652 gotit:
3653#if JOBS
3654 err_msg = "job %s not created under job control";
3655 if (getctl && jp->jobctl == 0)
3656 goto err;
3657#endif
3658 return jp;
3659 err:
3660 ash_msg_and_raise_error(err_msg, name);
3661}
3662
3663/*
3664 * Mark a job structure as unused.
3665 */
3666static void
3667freejob(struct job *jp)
3668{
3669 struct procstat *ps;
3670 int i;
3671
3672 INT_OFF;
3673 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003674 if (ps->ps_cmd != nullstr)
3675 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003676 }
3677 if (jp->ps != &jp->ps0)
3678 free(jp->ps);
3679 jp->used = 0;
3680 set_curjob(jp, CUR_DELETE);
3681 INT_ON;
3682}
3683
3684#if JOBS
3685static void
3686xtcsetpgrp(int fd, pid_t pgrp)
3687{
3688 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003689 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003690}
3691
3692/*
3693 * Turn job control on and off.
3694 *
3695 * Note: This code assumes that the third arg to ioctl is a character
3696 * pointer, which is true on Berkeley systems but not System V. Since
3697 * System V doesn't have job control yet, this isn't a problem now.
3698 *
3699 * Called with interrupts off.
3700 */
3701static void
3702setjobctl(int on)
3703{
3704 int fd;
3705 int pgrp;
3706
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003707 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003708 return;
3709 if (on) {
3710 int ofd;
3711 ofd = fd = open(_PATH_TTY, O_RDWR);
3712 if (fd < 0) {
3713 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3714 * That sometimes helps to acquire controlling tty.
3715 * Obviously, a workaround for bugs when someone
3716 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003717 fd = 2;
3718 while (!isatty(fd))
3719 if (--fd < 0)
3720 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003721 }
3722 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003723 if (ofd >= 0)
3724 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003725 if (fd < 0)
3726 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003727 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003728 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003729 do { /* while we are in the background */
3730 pgrp = tcgetpgrp(fd);
3731 if (pgrp < 0) {
3732 out:
3733 ash_msg("can't access tty; job control turned off");
3734 mflag = on = 0;
3735 goto close;
3736 }
3737 if (pgrp == getpgrp())
3738 break;
3739 killpg(0, SIGTTIN);
3740 } while (1);
3741 initialpgrp = pgrp;
3742
3743 setsignal(SIGTSTP);
3744 setsignal(SIGTTOU);
3745 setsignal(SIGTTIN);
3746 pgrp = rootpid;
3747 setpgid(0, pgrp);
3748 xtcsetpgrp(fd, pgrp);
3749 } else {
3750 /* turning job control off */
3751 fd = ttyfd;
3752 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003753 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003754 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003755 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003756 setpgid(0, pgrp);
3757 setsignal(SIGTSTP);
3758 setsignal(SIGTTOU);
3759 setsignal(SIGTTIN);
3760 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003761 if (fd >= 0)
3762 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003763 fd = -1;
3764 }
3765 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003766 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003767}
3768
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003769static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770killcmd(int argc, char **argv)
3771{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003772 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003773 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003774 do {
3775 if (argv[i][0] == '%') {
3776 struct job *jp = getjob(argv[i], 0);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003777 unsigned pid = jp->ps[0].ps_pid;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003778 /* Enough space for ' -NNN<nul>' */
3779 argv[i] = alloca(sizeof(int)*3 + 3);
3780 /* kill_main has matching code to expect
3781 * leading space. Needed to not confuse
3782 * negative pids with "kill -SIGNAL_NO" syntax */
3783 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003785 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003787 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788}
3789
3790static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003791showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003792{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003793 struct procstat *ps;
3794 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795
Denys Vlasenko285ad152009-12-04 23:02:27 +01003796 psend = jp->ps + jp->nprocs;
3797 for (ps = jp->ps + 1; ps < psend; ps++)
3798 printf(" | %s", ps->ps_cmd);
3799 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800 flush_stdout_stderr();
3801}
3802
3803
3804static int
3805restartjob(struct job *jp, int mode)
3806{
3807 struct procstat *ps;
3808 int i;
3809 int status;
3810 pid_t pgid;
3811
3812 INT_OFF;
3813 if (jp->state == JOBDONE)
3814 goto out;
3815 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003816 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003817 if (mode == FORK_FG)
3818 xtcsetpgrp(ttyfd, pgid);
3819 killpg(pgid, SIGCONT);
3820 ps = jp->ps;
3821 i = jp->nprocs;
3822 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003823 if (WIFSTOPPED(ps->ps_status)) {
3824 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003825 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003826 ps++;
3827 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003828 out:
3829 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3830 INT_ON;
3831 return status;
3832}
3833
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003834static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003835fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003836{
3837 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003838 int mode;
3839 int retval;
3840
3841 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3842 nextopt(nullstr);
3843 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003844 do {
3845 jp = getjob(*argv, 1);
3846 if (mode == FORK_BG) {
3847 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003848 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003849 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003850 out1str(jp->ps[0].ps_cmd);
3851 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003852 retval = restartjob(jp, mode);
3853 } while (*argv && *++argv);
3854 return retval;
3855}
3856#endif
3857
3858static int
3859sprint_status(char *s, int status, int sigonly)
3860{
3861 int col;
3862 int st;
3863
3864 col = 0;
3865 if (!WIFEXITED(status)) {
3866#if JOBS
3867 if (WIFSTOPPED(status))
3868 st = WSTOPSIG(status);
3869 else
3870#endif
3871 st = WTERMSIG(status);
3872 if (sigonly) {
3873 if (st == SIGINT || st == SIGPIPE)
3874 goto out;
3875#if JOBS
3876 if (WIFSTOPPED(status))
3877 goto out;
3878#endif
3879 }
3880 st &= 0x7f;
3881 col = fmtstr(s, 32, strsignal(st));
3882 if (WCOREDUMP(status)) {
3883 col += fmtstr(s + col, 16, " (core dumped)");
3884 }
3885 } else if (!sigonly) {
3886 st = WEXITSTATUS(status);
3887 if (st)
3888 col = fmtstr(s, 16, "Done(%d)", st);
3889 else
3890 col = fmtstr(s, 16, "Done");
3891 }
3892 out:
3893 return col;
3894}
3895
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003896static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003897dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003898{
3899 int pid;
3900 int status;
3901 struct job *jp;
3902 struct job *thisjob;
3903 int state;
3904
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003905 TRACE(("dowait(0x%x) called\n", wait_flags));
3906
3907 /* Do a wait system call. If job control is compiled in, we accept
3908 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3909 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003910 if (doing_jobctl)
3911 wait_flags |= WUNTRACED;
3912 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003913 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3914 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003915 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003916 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003917
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003918 INT_OFF;
3919 thisjob = NULL;
3920 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003921 struct procstat *ps;
3922 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003923 if (jp->state == JOBDONE)
3924 continue;
3925 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003926 ps = jp->ps;
3927 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003928 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003929 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003930 TRACE(("Job %d: changing status of proc %d "
3931 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003932 jobno(jp), pid, ps->ps_status, status));
3933 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003934 thisjob = jp;
3935 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003936 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003937 state = JOBRUNNING;
3938#if JOBS
3939 if (state == JOBRUNNING)
3940 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003941 if (WIFSTOPPED(ps->ps_status)) {
3942 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003943 state = JOBSTOPPED;
3944 }
3945#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003946 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003947 if (thisjob)
3948 goto gotjob;
3949 }
3950#if JOBS
3951 if (!WIFSTOPPED(status))
3952#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003953 jobless--;
3954 goto out;
3955
3956 gotjob:
3957 if (state != JOBRUNNING) {
3958 thisjob->changed = 1;
3959
3960 if (thisjob->state != state) {
3961 TRACE(("Job %d: changing state from %d to %d\n",
3962 jobno(thisjob), thisjob->state, state));
3963 thisjob->state = state;
3964#if JOBS
3965 if (state == JOBSTOPPED) {
3966 set_curjob(thisjob, CUR_STOPPED);
3967 }
3968#endif
3969 }
3970 }
3971
3972 out:
3973 INT_ON;
3974
3975 if (thisjob && thisjob == job) {
3976 char s[48 + 1];
3977 int len;
3978
3979 len = sprint_status(s, status, 1);
3980 if (len) {
3981 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003982 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003983 out2str(s);
3984 }
3985 }
3986 return pid;
3987}
3988
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003989static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003990blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003991{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003992 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003993 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003994 raise_exception(EXSIG);
3995 return pid;
3996}
3997
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003998#if JOBS
3999static void
4000showjob(FILE *out, struct job *jp, int mode)
4001{
4002 struct procstat *ps;
4003 struct procstat *psend;
4004 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004005 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004006 char s[80];
4007
4008 ps = jp->ps;
4009
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004010 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004011 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004012 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004013 return;
4014 }
4015
4016 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004017 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004018
4019 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004020 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004021 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004022 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004023
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004024 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004025 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004026
4027 psend = ps + jp->nprocs;
4028
4029 if (jp->state == JOBRUNNING) {
4030 strcpy(s + col, "Running");
4031 col += sizeof("Running") - 1;
4032 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004033 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034 if (jp->state == JOBSTOPPED)
4035 status = jp->stopstatus;
4036 col += sprint_status(s + col, status, 0);
4037 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004038 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004039
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004040 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4041 * or prints several "PID | <cmdN>" lines,
4042 * depending on SHOW_PIDS bit.
4043 * We do not print status of individual processes
4044 * between PID and <cmdN>. bash does it, but not very well:
4045 * first line shows overall job status, not process status,
4046 * making it impossible to know 1st process status.
4047 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004048 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004049 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004051 s[0] = '\0';
4052 col = 33;
4053 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004054 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004055 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004056 fprintf(out, "%s%*c%s%s",
4057 s,
4058 33 - col >= 0 ? 33 - col : 0, ' ',
4059 ps == jp->ps ? "" : "| ",
4060 ps->ps_cmd
4061 );
4062 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004063 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064
4065 jp->changed = 0;
4066
4067 if (jp->state == JOBDONE) {
4068 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4069 freejob(jp);
4070 }
4071}
4072
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004073/*
4074 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4075 * statuses have changed since the last call to showjobs.
4076 */
4077static void
4078showjobs(FILE *out, int mode)
4079{
4080 struct job *jp;
4081
Denys Vlasenko883cea42009-07-11 15:31:59 +02004082 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004083
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004084 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004085 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004086 continue;
4087
4088 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004089 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004090 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004091 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004092 }
4093}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004094
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004095static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004096jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004097{
4098 int mode, m;
4099
4100 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004101 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004102 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004103 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004104 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004105 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004106 }
4107
4108 argv = argptr;
4109 if (*argv) {
4110 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004111 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004112 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004113 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004114 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004115 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004116
4117 return 0;
4118}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004119#endif /* JOBS */
4120
Michael Abbott359da5e2009-12-04 23:03:29 +01004121/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004122static int
4123getstatus(struct job *job)
4124{
4125 int status;
4126 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004127 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004128
Michael Abbott359da5e2009-12-04 23:03:29 +01004129 /* Fetch last member's status */
4130 ps = job->ps + job->nprocs - 1;
4131 status = ps->ps_status;
4132 if (pipefail) {
4133 /* "set -o pipefail" mode: use last _nonzero_ status */
4134 while (status == 0 && --ps >= job->ps)
4135 status = ps->ps_status;
4136 }
4137
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004138 retval = WEXITSTATUS(status);
4139 if (!WIFEXITED(status)) {
4140#if JOBS
4141 retval = WSTOPSIG(status);
4142 if (!WIFSTOPPED(status))
4143#endif
4144 {
4145 /* XXX: limits number of signals */
4146 retval = WTERMSIG(status);
4147#if JOBS
4148 if (retval == SIGINT)
4149 job->sigint = 1;
4150#endif
4151 }
4152 retval += 128;
4153 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004154 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004155 jobno(job), job->nprocs, status, retval));
4156 return retval;
4157}
4158
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004159static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004160waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004161{
4162 struct job *job;
4163 int retval;
4164 struct job *jp;
4165
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004166 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004167 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004168
4169 nextopt(nullstr);
4170 retval = 0;
4171
4172 argv = argptr;
4173 if (!*argv) {
4174 /* wait for all jobs */
4175 for (;;) {
4176 jp = curjob;
4177 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004178 if (!jp) /* no running procs */
4179 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004180 if (jp->state == JOBRUNNING)
4181 break;
4182 jp->waited = 1;
4183 jp = jp->prev_job;
4184 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004185 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004186 /* man bash:
4187 * "When bash is waiting for an asynchronous command via
4188 * the wait builtin, the reception of a signal for which a trap
4189 * has been set will cause the wait builtin to return immediately
4190 * with an exit status greater than 128, immediately after which
4191 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004192 *
4193 * blocking_wait_with_raise_on_sig raises signal handlers
4194 * if it gets no pid (pid < 0). However,
4195 * if child sends us a signal *and immediately exits*,
4196 * blocking_wait_with_raise_on_sig gets pid > 0
4197 * and does not handle pending_sig. Check this case: */
4198 if (pending_sig)
4199 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004200 }
4201 }
4202
4203 retval = 127;
4204 do {
4205 if (**argv != '%') {
4206 pid_t pid = number(*argv);
4207 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004208 while (1) {
4209 if (!job)
4210 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004211 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004212 break;
4213 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004214 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004215 } else
4216 job = getjob(*argv, 0);
4217 /* loop until process terminated or stopped */
4218 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004219 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004220 job->waited = 1;
4221 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004222 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004223 } while (*++argv);
4224
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004225 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004226 return retval;
4227}
4228
4229static struct job *
4230growjobtab(void)
4231{
4232 size_t len;
4233 ptrdiff_t offset;
4234 struct job *jp, *jq;
4235
4236 len = njobs * sizeof(*jp);
4237 jq = jobtab;
4238 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4239
4240 offset = (char *)jp - (char *)jq;
4241 if (offset) {
4242 /* Relocate pointers */
4243 size_t l = len;
4244
4245 jq = (struct job *)((char *)jq + l);
4246 while (l) {
4247 l -= sizeof(*jp);
4248 jq--;
4249#define joff(p) ((struct job *)((char *)(p) + l))
4250#define jmove(p) (p) = (void *)((char *)(p) + offset)
4251 if (joff(jp)->ps == &jq->ps0)
4252 jmove(joff(jp)->ps);
4253 if (joff(jp)->prev_job)
4254 jmove(joff(jp)->prev_job);
4255 }
4256 if (curjob)
4257 jmove(curjob);
4258#undef joff
4259#undef jmove
4260 }
4261
4262 njobs += 4;
4263 jobtab = jp;
4264 jp = (struct job *)((char *)jp + len);
4265 jq = jp + 3;
4266 do {
4267 jq->used = 0;
4268 } while (--jq >= jp);
4269 return jp;
4270}
4271
4272/*
4273 * Return a new job structure.
4274 * Called with interrupts off.
4275 */
4276static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004277makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004278{
4279 int i;
4280 struct job *jp;
4281
4282 for (i = njobs, jp = jobtab; ; jp++) {
4283 if (--i < 0) {
4284 jp = growjobtab();
4285 break;
4286 }
4287 if (jp->used == 0)
4288 break;
4289 if (jp->state != JOBDONE || !jp->waited)
4290 continue;
4291#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004292 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004293 continue;
4294#endif
4295 freejob(jp);
4296 break;
4297 }
4298 memset(jp, 0, sizeof(*jp));
4299#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004300 /* jp->jobctl is a bitfield.
4301 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004302 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004303 jp->jobctl = 1;
4304#endif
4305 jp->prev_job = curjob;
4306 curjob = jp;
4307 jp->used = 1;
4308 jp->ps = &jp->ps0;
4309 if (nprocs > 1) {
4310 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4311 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004312 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004313 jobno(jp)));
4314 return jp;
4315}
4316
4317#if JOBS
4318/*
4319 * Return a string identifying a command (to be printed by the
4320 * jobs command).
4321 */
4322static char *cmdnextc;
4323
4324static void
4325cmdputs(const char *s)
4326{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004327 static const char vstype[VSTYPE + 1][3] = {
4328 "", "}", "-", "+", "?", "=",
4329 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004330 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004331 };
4332
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004333 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004334 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004335 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004336 unsigned char c;
4337 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004338 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004339
Denys Vlasenko46a14772009-12-10 21:27:13 +01004340 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004341 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4342 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004343 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004344 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004345 switch (c) {
4346 case CTLESC:
4347 c = *p++;
4348 break;
4349 case CTLVAR:
4350 subtype = *p++;
4351 if ((subtype & VSTYPE) == VSLENGTH)
4352 str = "${#";
4353 else
4354 str = "${";
4355 if (!(subtype & VSQUOTE) == !(quoted & 1))
4356 goto dostr;
4357 quoted ^= 1;
4358 c = '"';
4359 break;
4360 case CTLENDVAR:
4361 str = "\"}" + !(quoted & 1);
4362 quoted >>= 1;
4363 subtype = 0;
4364 goto dostr;
4365 case CTLBACKQ:
4366 str = "$(...)";
4367 goto dostr;
4368 case CTLBACKQ+CTLQUOTE:
4369 str = "\"$(...)\"";
4370 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004371#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004372 case CTLARI:
4373 str = "$((";
4374 goto dostr;
4375 case CTLENDARI:
4376 str = "))";
4377 goto dostr;
4378#endif
4379 case CTLQUOTEMARK:
4380 quoted ^= 1;
4381 c = '"';
4382 break;
4383 case '=':
4384 if (subtype == 0)
4385 break;
4386 if ((subtype & VSTYPE) != VSNORMAL)
4387 quoted <<= 1;
4388 str = vstype[subtype & VSTYPE];
4389 if (subtype & VSNUL)
4390 c = ':';
4391 else
4392 goto checkstr;
4393 break;
4394 case '\'':
4395 case '\\':
4396 case '"':
4397 case '$':
4398 /* These can only happen inside quotes */
4399 cc[0] = c;
4400 str = cc;
4401 c = '\\';
4402 break;
4403 default:
4404 break;
4405 }
4406 USTPUTC(c, nextc);
4407 checkstr:
4408 if (!str)
4409 continue;
4410 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004411 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004412 USTPUTC(c, nextc);
4413 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004414 } /* while *p++ not NUL */
4415
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004416 if (quoted & 1) {
4417 USTPUTC('"', nextc);
4418 }
4419 *nextc = 0;
4420 cmdnextc = nextc;
4421}
4422
4423/* cmdtxt() and cmdlist() call each other */
4424static void cmdtxt(union node *n);
4425
4426static void
4427cmdlist(union node *np, int sep)
4428{
4429 for (; np; np = np->narg.next) {
4430 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004431 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004432 cmdtxt(np);
4433 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004434 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004435 }
4436}
4437
4438static void
4439cmdtxt(union node *n)
4440{
4441 union node *np;
4442 struct nodelist *lp;
4443 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004444
4445 if (!n)
4446 return;
4447 switch (n->type) {
4448 default:
4449#if DEBUG
4450 abort();
4451#endif
4452 case NPIPE:
4453 lp = n->npipe.cmdlist;
4454 for (;;) {
4455 cmdtxt(lp->n);
4456 lp = lp->next;
4457 if (!lp)
4458 break;
4459 cmdputs(" | ");
4460 }
4461 break;
4462 case NSEMI:
4463 p = "; ";
4464 goto binop;
4465 case NAND:
4466 p = " && ";
4467 goto binop;
4468 case NOR:
4469 p = " || ";
4470 binop:
4471 cmdtxt(n->nbinary.ch1);
4472 cmdputs(p);
4473 n = n->nbinary.ch2;
4474 goto donode;
4475 case NREDIR:
4476 case NBACKGND:
4477 n = n->nredir.n;
4478 goto donode;
4479 case NNOT:
4480 cmdputs("!");
4481 n = n->nnot.com;
4482 donode:
4483 cmdtxt(n);
4484 break;
4485 case NIF:
4486 cmdputs("if ");
4487 cmdtxt(n->nif.test);
4488 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004490 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004491 cmdputs("; else ");
4492 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004493 } else {
4494 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004495 }
4496 p = "; fi";
4497 goto dotail;
4498 case NSUBSHELL:
4499 cmdputs("(");
4500 n = n->nredir.n;
4501 p = ")";
4502 goto dotail;
4503 case NWHILE:
4504 p = "while ";
4505 goto until;
4506 case NUNTIL:
4507 p = "until ";
4508 until:
4509 cmdputs(p);
4510 cmdtxt(n->nbinary.ch1);
4511 n = n->nbinary.ch2;
4512 p = "; done";
4513 dodo:
4514 cmdputs("; do ");
4515 dotail:
4516 cmdtxt(n);
4517 goto dotail2;
4518 case NFOR:
4519 cmdputs("for ");
4520 cmdputs(n->nfor.var);
4521 cmdputs(" in ");
4522 cmdlist(n->nfor.args, 1);
4523 n = n->nfor.body;
4524 p = "; done";
4525 goto dodo;
4526 case NDEFUN:
4527 cmdputs(n->narg.text);
4528 p = "() { ... }";
4529 goto dotail2;
4530 case NCMD:
4531 cmdlist(n->ncmd.args, 1);
4532 cmdlist(n->ncmd.redirect, 0);
4533 break;
4534 case NARG:
4535 p = n->narg.text;
4536 dotail2:
4537 cmdputs(p);
4538 break;
4539 case NHERE:
4540 case NXHERE:
4541 p = "<<...";
4542 goto dotail2;
4543 case NCASE:
4544 cmdputs("case ");
4545 cmdputs(n->ncase.expr->narg.text);
4546 cmdputs(" in ");
4547 for (np = n->ncase.cases; np; np = np->nclist.next) {
4548 cmdtxt(np->nclist.pattern);
4549 cmdputs(") ");
4550 cmdtxt(np->nclist.body);
4551 cmdputs(";; ");
4552 }
4553 p = "esac";
4554 goto dotail2;
4555 case NTO:
4556 p = ">";
4557 goto redir;
4558 case NCLOBBER:
4559 p = ">|";
4560 goto redir;
4561 case NAPPEND:
4562 p = ">>";
4563 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004564#if ENABLE_ASH_BASH_COMPAT
4565 case NTO2:
4566#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004567 case NTOFD:
4568 p = ">&";
4569 goto redir;
4570 case NFROM:
4571 p = "<";
4572 goto redir;
4573 case NFROMFD:
4574 p = "<&";
4575 goto redir;
4576 case NFROMTO:
4577 p = "<>";
4578 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004579 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004580 cmdputs(p);
4581 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004582 cmdputs(utoa(n->ndup.dupfd));
4583 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004584 }
4585 n = n->nfile.fname;
4586 goto donode;
4587 }
4588}
4589
4590static char *
4591commandtext(union node *n)
4592{
4593 char *name;
4594
4595 STARTSTACKSTR(cmdnextc);
4596 cmdtxt(n);
4597 name = stackblock();
4598 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4599 name, cmdnextc, cmdnextc));
4600 return ckstrdup(name);
4601}
4602#endif /* JOBS */
4603
4604/*
4605 * Fork off a subshell. If we are doing job control, give the subshell its
4606 * own process group. Jp is a job structure that the job is to be added to.
4607 * N is the command that will be evaluated by the child. Both jp and n may
4608 * be NULL. The mode parameter can be one of the following:
4609 * FORK_FG - Fork off a foreground process.
4610 * FORK_BG - Fork off a background process.
4611 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4612 * process group even if job control is on.
4613 *
4614 * When job control is turned off, background processes have their standard
4615 * input redirected to /dev/null (except for the second and later processes
4616 * in a pipeline).
4617 *
4618 * Called with interrupts off.
4619 */
4620/*
4621 * Clear traps on a fork.
4622 */
4623static void
4624clear_traps(void)
4625{
4626 char **tp;
4627
4628 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004629 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004630 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004631 if (trap_ptr == trap)
4632 free(*tp);
4633 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004634 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004635 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004636 setsignal(tp - trap);
4637 INT_ON;
4638 }
4639 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004640 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004641}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004642
4643/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004644static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004645
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004646/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004647static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004648forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004649{
4650 int oldlvl;
4651
4652 TRACE(("Child shell %d\n", getpid()));
4653 oldlvl = shlvl;
4654 shlvl++;
4655
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004656 /* man bash: "Non-builtin commands run by bash have signal handlers
4657 * set to the values inherited by the shell from its parent".
4658 * Do we do it correctly? */
4659
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004660 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004661
4662 if (mode == FORK_NOJOB /* is it `xxx` ? */
4663 && n && n->type == NCMD /* is it single cmd? */
4664 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004665 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004666 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4667 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4668 ) {
4669 TRACE(("Trap hack\n"));
4670 /* Awful hack for `trap` or $(trap).
4671 *
4672 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4673 * contains an example where "trap" is executed in a subshell:
4674 *
4675 * save_traps=$(trap)
4676 * ...
4677 * eval "$save_traps"
4678 *
4679 * Standard does not say that "trap" in subshell shall print
4680 * parent shell's traps. It only says that its output
4681 * must have suitable form, but then, in the above example
4682 * (which is not supposed to be normative), it implies that.
4683 *
4684 * bash (and probably other shell) does implement it
4685 * (traps are reset to defaults, but "trap" still shows them),
4686 * but as a result, "trap" logic is hopelessly messed up:
4687 *
4688 * # trap
4689 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4690 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4691 * # true | trap <--- trap is in subshell - no output (ditto)
4692 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4693 * trap -- 'echo Ho' SIGWINCH
4694 * # echo `(trap)` <--- in subshell in subshell - output
4695 * trap -- 'echo Ho' SIGWINCH
4696 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4697 * trap -- 'echo Ho' SIGWINCH
4698 *
4699 * The rules when to forget and when to not forget traps
4700 * get really complex and nonsensical.
4701 *
4702 * Our solution: ONLY bare $(trap) or `trap` is special.
4703 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004704 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004705 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004706 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004707 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004708 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004709#if JOBS
4710 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004711 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004712 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4713 pid_t pgrp;
4714
4715 if (jp->nprocs == 0)
4716 pgrp = getpid();
4717 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004718 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004719 /* this can fail because we are doing it in the parent also */
4720 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004721 if (mode == FORK_FG)
4722 xtcsetpgrp(ttyfd, pgrp);
4723 setsignal(SIGTSTP);
4724 setsignal(SIGTTOU);
4725 } else
4726#endif
4727 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004728 /* man bash: "When job control is not in effect,
4729 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004730 ignoresig(SIGINT);
4731 ignoresig(SIGQUIT);
4732 if (jp->nprocs == 0) {
4733 close(0);
4734 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004735 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004736 }
4737 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004738 if (!oldlvl) {
4739 if (iflag) { /* why if iflag only? */
4740 setsignal(SIGINT);
4741 setsignal(SIGTERM);
4742 }
4743 /* man bash:
4744 * "In all cases, bash ignores SIGQUIT. Non-builtin
4745 * commands run by bash have signal handlers
4746 * set to the values inherited by the shell
4747 * from its parent".
4748 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004749 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004750 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004751#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004752 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004753 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004754 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004755 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004756 /* "jobs": we do not want to clear job list for it,
4757 * instead we remove only _its_ own_ job from job list.
4758 * This makes "jobs .... | cat" more useful.
4759 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004760 freejob(curjob);
4761 return;
4762 }
4763#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004764 for (jp = curjob; jp; jp = jp->prev_job)
4765 freejob(jp);
4766 jobless = 0;
4767}
4768
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004769/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004770#if !JOBS
4771#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4772#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004773static void
4774forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4775{
4776 TRACE(("In parent shell: child = %d\n", pid));
4777 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004778 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4779 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004780 jobless++;
4781 return;
4782 }
4783#if JOBS
4784 if (mode != FORK_NOJOB && jp->jobctl) {
4785 int pgrp;
4786
4787 if (jp->nprocs == 0)
4788 pgrp = pid;
4789 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004790 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004791 /* This can fail because we are doing it in the child also */
4792 setpgid(pid, pgrp);
4793 }
4794#endif
4795 if (mode == FORK_BG) {
4796 backgndpid = pid; /* set $! */
4797 set_curjob(jp, CUR_RUNNING);
4798 }
4799 if (jp) {
4800 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004801 ps->ps_pid = pid;
4802 ps->ps_status = -1;
4803 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004804#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004805 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004806 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004807#endif
4808 }
4809}
4810
4811static int
4812forkshell(struct job *jp, union node *n, int mode)
4813{
4814 int pid;
4815
4816 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4817 pid = fork();
4818 if (pid < 0) {
4819 TRACE(("Fork failed, errno=%d", errno));
4820 if (jp)
4821 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004822 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004823 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004824 if (pid == 0) {
4825 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004826 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004827 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004828 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004829 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004830 return pid;
4831}
4832
4833/*
4834 * Wait for job to finish.
4835 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004836 * Under job control we have the problem that while a child process
4837 * is running interrupts generated by the user are sent to the child
4838 * but not to the shell. This means that an infinite loop started by
4839 * an interactive user may be hard to kill. With job control turned off,
4840 * an interactive user may place an interactive program inside a loop.
4841 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004842 * these interrupts to also abort the loop. The approach we take here
4843 * is to have the shell ignore interrupt signals while waiting for a
4844 * foreground process to terminate, and then send itself an interrupt
4845 * signal if the child process was terminated by an interrupt signal.
4846 * Unfortunately, some programs want to do a bit of cleanup and then
4847 * exit on interrupt; unless these processes terminate themselves by
4848 * sending a signal to themselves (instead of calling exit) they will
4849 * confuse this approach.
4850 *
4851 * Called with interrupts off.
4852 */
4853static int
4854waitforjob(struct job *jp)
4855{
4856 int st;
4857
4858 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004859
4860 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004861 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004862 /* In non-interactive shells, we _can_ get
4863 * a keyboard signal here and be EINTRed,
4864 * but we just loop back, waiting for command to complete.
4865 *
4866 * man bash:
4867 * "If bash is waiting for a command to complete and receives
4868 * a signal for which a trap has been set, the trap
4869 * will not be executed until the command completes."
4870 *
4871 * Reality is that even if trap is not set, bash
4872 * will not act on the signal until command completes.
4873 * Try this. sleep5intoff.c:
4874 * #include <signal.h>
4875 * #include <unistd.h>
4876 * int main() {
4877 * sigset_t set;
4878 * sigemptyset(&set);
4879 * sigaddset(&set, SIGINT);
4880 * sigaddset(&set, SIGQUIT);
4881 * sigprocmask(SIG_BLOCK, &set, NULL);
4882 * sleep(5);
4883 * return 0;
4884 * }
4885 * $ bash -c './sleep5intoff; echo hi'
4886 * ^C^C^C^C <--- pressing ^C once a second
4887 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004888 * $ bash -c './sleep5intoff; echo hi'
4889 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4890 * $ _
4891 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004892 dowait(DOWAIT_BLOCK, jp);
4893 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004894 INT_ON;
4895
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004896 st = getstatus(jp);
4897#if JOBS
4898 if (jp->jobctl) {
4899 xtcsetpgrp(ttyfd, rootpid);
4900 /*
4901 * This is truly gross.
4902 * If we're doing job control, then we did a TIOCSPGRP which
4903 * caused us (the shell) to no longer be in the controlling
4904 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4905 * intuit from the subprocess exit status whether a SIGINT
4906 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4907 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004908 if (jp->sigint) /* TODO: do the same with all signals */
4909 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004910 }
4911 if (jp->state == JOBDONE)
4912#endif
4913 freejob(jp);
4914 return st;
4915}
4916
4917/*
4918 * return 1 if there are stopped jobs, otherwise 0
4919 */
4920static int
4921stoppedjobs(void)
4922{
4923 struct job *jp;
4924 int retval;
4925
4926 retval = 0;
4927 if (job_warning)
4928 goto out;
4929 jp = curjob;
4930 if (jp && jp->state == JOBSTOPPED) {
4931 out2str("You have stopped jobs.\n");
4932 job_warning = 2;
4933 retval++;
4934 }
4935 out:
4936 return retval;
4937}
4938
4939
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004940/* ============ redir.c
4941 *
4942 * Code for dealing with input/output redirection.
4943 */
4944
4945#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004946#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004947
4948/*
4949 * Open a file in noclobber mode.
4950 * The code was copied from bash.
4951 */
4952static int
4953noclobberopen(const char *fname)
4954{
4955 int r, fd;
4956 struct stat finfo, finfo2;
4957
4958 /*
4959 * If the file exists and is a regular file, return an error
4960 * immediately.
4961 */
4962 r = stat(fname, &finfo);
4963 if (r == 0 && S_ISREG(finfo.st_mode)) {
4964 errno = EEXIST;
4965 return -1;
4966 }
4967
4968 /*
4969 * If the file was not present (r != 0), make sure we open it
4970 * exclusively so that if it is created before we open it, our open
4971 * will fail. Make sure that we do not truncate an existing file.
4972 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4973 * file was not a regular file, we leave O_EXCL off.
4974 */
4975 if (r != 0)
4976 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4977 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4978
4979 /* If the open failed, return the file descriptor right away. */
4980 if (fd < 0)
4981 return fd;
4982
4983 /*
4984 * OK, the open succeeded, but the file may have been changed from a
4985 * non-regular file to a regular file between the stat and the open.
4986 * We are assuming that the O_EXCL open handles the case where FILENAME
4987 * did not exist and is symlinked to an existing file between the stat
4988 * and open.
4989 */
4990
4991 /*
4992 * If we can open it and fstat the file descriptor, and neither check
4993 * revealed that it was a regular file, and the file has not been
4994 * replaced, return the file descriptor.
4995 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02004996 if (fstat(fd, &finfo2) == 0
4997 && !S_ISREG(finfo2.st_mode)
4998 && finfo.st_dev == finfo2.st_dev
4999 && finfo.st_ino == finfo2.st_ino
5000 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005001 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005002 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005003
5004 /* The file has been replaced. badness. */
5005 close(fd);
5006 errno = EEXIST;
5007 return -1;
5008}
5009
5010/*
5011 * Handle here documents. Normally we fork off a process to write the
5012 * data to a pipe. If the document is short, we can stuff the data in
5013 * the pipe without forking.
5014 */
5015/* openhere needs this forward reference */
5016static void expandhere(union node *arg, int fd);
5017static int
5018openhere(union node *redir)
5019{
5020 int pip[2];
5021 size_t len = 0;
5022
5023 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005024 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005025 if (redir->type == NHERE) {
5026 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005027 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005028 full_write(pip[1], redir->nhere.doc->narg.text, len);
5029 goto out;
5030 }
5031 }
5032 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005033 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005034 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005035 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5036 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5037 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5038 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005039 signal(SIGPIPE, SIG_DFL);
5040 if (redir->type == NHERE)
5041 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005042 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005043 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005044 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 }
5046 out:
5047 close(pip[1]);
5048 return pip[0];
5049}
5050
5051static int
5052openredirect(union node *redir)
5053{
5054 char *fname;
5055 int f;
5056
5057 switch (redir->nfile.type) {
5058 case NFROM:
5059 fname = redir->nfile.expfname;
5060 f = open(fname, O_RDONLY);
5061 if (f < 0)
5062 goto eopen;
5063 break;
5064 case NFROMTO:
5065 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005066 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005067 if (f < 0)
5068 goto ecreate;
5069 break;
5070 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005071#if ENABLE_ASH_BASH_COMPAT
5072 case NTO2:
5073#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005074 /* Take care of noclobber mode. */
5075 if (Cflag) {
5076 fname = redir->nfile.expfname;
5077 f = noclobberopen(fname);
5078 if (f < 0)
5079 goto ecreate;
5080 break;
5081 }
5082 /* FALLTHROUGH */
5083 case NCLOBBER:
5084 fname = redir->nfile.expfname;
5085 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5086 if (f < 0)
5087 goto ecreate;
5088 break;
5089 case NAPPEND:
5090 fname = redir->nfile.expfname;
5091 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5092 if (f < 0)
5093 goto ecreate;
5094 break;
5095 default:
5096#if DEBUG
5097 abort();
5098#endif
5099 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005100/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005101// case NTOFD:
5102// case NFROMFD:
5103// f = -1;
5104// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005105 case NHERE:
5106 case NXHERE:
5107 f = openhere(redir);
5108 break;
5109 }
5110
5111 return f;
5112 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005113 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005114 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005115 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005116}
5117
5118/*
5119 * Copy a file descriptor to be >= to. Returns -1
5120 * if the source file descriptor is closed, EMPTY if there are no unused
5121 * file descriptors left.
5122 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005123/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5124 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005125enum {
5126 COPYFD_EXACT = (int)~(INT_MAX),
5127 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5128};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005129static int
5130copyfd(int from, int to)
5131{
5132 int newfd;
5133
Denis Vlasenko5a867312008-07-24 19:46:38 +00005134 if (to & COPYFD_EXACT) {
5135 to &= ~COPYFD_EXACT;
5136 /*if (from != to)*/
5137 newfd = dup2(from, to);
5138 } else {
5139 newfd = fcntl(from, F_DUPFD, to);
5140 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005141 if (newfd < 0) {
5142 if (errno == EMFILE)
5143 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005144 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005145 ash_msg_and_raise_error("%d: %m", from);
5146 }
5147 return newfd;
5148}
5149
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005150/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005151struct two_fd_t {
5152 int orig, copy;
5153};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005154struct redirtab {
5155 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005156 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005157 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005158 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005159};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005160#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005161
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005162static int need_to_remember(struct redirtab *rp, int fd)
5163{
5164 int i;
5165
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005166 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005167 return 0;
5168
5169 for (i = 0; i < rp->pair_count; i++) {
5170 if (rp->two_fd[i].orig == fd) {
5171 /* already remembered */
5172 return 0;
5173 }
5174 }
5175 return 1;
5176}
5177
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005178/* "hidden" fd is a fd used to read scripts, or a copy of such */
5179static int is_hidden_fd(struct redirtab *rp, int fd)
5180{
5181 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005182 struct parsefile *pf;
5183
5184 if (fd == -1)
5185 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005186 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005187 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005188 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005189 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005190 * $ ash # running ash interactively
5191 * $ . ./script.sh
5192 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005193 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005194 * it's still ok to use it: "read" builtin uses it,
5195 * why should we cripple "exec" builtin?
5196 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005197 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005198 return 1;
5199 }
5200 pf = pf->prev;
5201 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005202
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005203 if (!rp)
5204 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005205 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005206 fd |= COPYFD_RESTORE;
5207 for (i = 0; i < rp->pair_count; i++) {
5208 if (rp->two_fd[i].copy == fd) {
5209 return 1;
5210 }
5211 }
5212 return 0;
5213}
5214
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005215/*
5216 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5217 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005218 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005219 */
5220/* flags passed to redirect */
5221#define REDIR_PUSH 01 /* save previous values of file descriptors */
5222#define REDIR_SAVEFD2 03 /* set preverrout */
5223static void
5224redirect(union node *redir, int flags)
5225{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005226 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005227 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005228 int i;
5229 int fd;
5230 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005231 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005232
Denis Vlasenko01631112007-12-16 17:20:38 +00005233 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005234 if (!redir) {
5235 return;
5236 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005237
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005238 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005239 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005240 INT_OFF;
5241 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005242 union node *tmp = redir;
5243 do {
5244 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005245#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005246 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005247 sv_pos++;
5248#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005249 tmp = tmp->nfile.next;
5250 } while (tmp);
5251 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005252 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005253 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005254 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005255 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005256 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005257 while (sv_pos > 0) {
5258 sv_pos--;
5259 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5260 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005261 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005262
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005263 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005264 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005265 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005266 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005267 right_fd = redir->ndup.dupfd;
5268 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005269 /* redirect from/to same file descriptor? */
5270 if (right_fd == fd)
5271 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005272 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005273 if (is_hidden_fd(sv, right_fd)) {
5274 errno = EBADF; /* as if it is closed */
5275 ash_msg_and_raise_error("%d: %m", right_fd);
5276 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005277 newfd = -1;
5278 } else {
5279 newfd = openredirect(redir); /* always >= 0 */
5280 if (fd == newfd) {
5281 /* Descriptor wasn't open before redirect.
5282 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005283 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005284 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005285 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005286 continue;
5287 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005288 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005289#if ENABLE_ASH_BASH_COMPAT
5290 redirect_more:
5291#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005292 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005293 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005294 /* Careful to not accidentally "save"
5295 * to the same fd as right side fd in N>&M */
5296 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5297 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005298/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5299 * are closed in popredir() in the child, preventing them from leaking
5300 * into child. (popredir() also cleans up the mess in case of failures)
5301 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005302 if (i == -1) {
5303 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005304 if (i != EBADF) {
5305 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005306 if (newfd >= 0)
5307 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005308 errno = i;
5309 ash_msg_and_raise_error("%d: %m", fd);
5310 /* NOTREACHED */
5311 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005312 /* EBADF: it is not open - good, remember to close it */
5313 remember_to_close:
5314 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005315 } else { /* fd is open, save its copy */
5316 /* "exec fd>&-" should not close fds
5317 * which point to script file(s).
5318 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005319 if (is_hidden_fd(sv, fd))
5320 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005321 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005322 if (fd == 2)
5323 copied_fd2 = i;
5324 sv->two_fd[sv_pos].orig = fd;
5325 sv->two_fd[sv_pos].copy = i;
5326 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005327 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005328 if (newfd < 0) {
5329 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005330 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005331 /* Don't want to trigger debugging */
5332 if (fd != -1)
5333 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005334 } else {
5335 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005336 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005337 } else if (fd != newfd) { /* move newfd to fd */
5338 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005339#if ENABLE_ASH_BASH_COMPAT
5340 if (!(redir->nfile.type == NTO2 && fd == 2))
5341#endif
5342 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005343 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005344#if ENABLE_ASH_BASH_COMPAT
5345 if (redir->nfile.type == NTO2 && fd == 1) {
5346 /* We already redirected it to fd 1, now copy it to 2 */
5347 newfd = 1;
5348 fd = 2;
5349 goto redirect_more;
5350 }
5351#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005352 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005353
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005354 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005355 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5356 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005357}
5358
5359/*
5360 * Undo the effects of the last redirection.
5361 */
5362static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005363popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005364{
5365 struct redirtab *rp;
5366 int i;
5367
Denis Vlasenko01631112007-12-16 17:20:38 +00005368 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005369 return;
5370 INT_OFF;
5371 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005372 for (i = 0; i < rp->pair_count; i++) {
5373 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005374 int copy = rp->two_fd[i].copy;
5375 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005376 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005377 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005378 continue;
5379 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005380 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005381 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005382 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005383 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005384 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005385 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005386 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005387 }
5388 }
5389 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005390 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005391 free(rp);
5392 INT_ON;
5393}
5394
5395/*
5396 * Undo all redirections. Called on error or interrupt.
5397 */
5398
5399/*
5400 * Discard all saved file descriptors.
5401 */
5402static void
5403clearredir(int drop)
5404{
5405 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005406 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005407 if (!redirlist)
5408 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005409 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005410 }
5411}
5412
5413static int
5414redirectsafe(union node *redir, int flags)
5415{
5416 int err;
5417 volatile int saveint;
5418 struct jmploc *volatile savehandler = exception_handler;
5419 struct jmploc jmploc;
5420
5421 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005422 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5423 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005424 if (!err) {
5425 exception_handler = &jmploc;
5426 redirect(redir, flags);
5427 }
5428 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005429 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005430 longjmp(exception_handler->loc, 1);
5431 RESTORE_INT(saveint);
5432 return err;
5433}
5434
5435
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005436/* ============ Routines to expand arguments to commands
5437 *
5438 * We have to deal with backquotes, shell variables, and file metacharacters.
5439 */
5440
Mike Frysinger98c52642009-04-02 10:02:37 +00005441#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005442static arith_t
5443ash_arith(const char *s)
5444{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005445 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005446 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005447
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005448 math_state.lookupvar = lookupvar;
5449 math_state.setvar = setvar2;
5450 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005451
5452 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005453 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005454 if (math_state.errmsg)
5455 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005456 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);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005519 len = fmtstr(expdest, 32, ARITH_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 Vlasenko6040fe82010-09-12 15:03:16 +02006004 if ((unsigned char)*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)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006018 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006019 ) {
6020 /* c == '=' || c == ':' || c == CTLENDARI */
6021 length++;
6022 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023 }
6024 if (length > 0) {
6025 int newloc;
6026 expdest = stack_nputstr(p, length, expdest);
6027 newloc = expdest - (char *)stackblock();
6028 if (breakall && !inquotes && newloc > startloc) {
6029 recordregion(startloc, newloc, 0);
6030 }
6031 startloc = newloc;
6032 }
6033 p += length + 1;
6034 length = 0;
6035
6036 switch (c) {
6037 case '\0':
6038 goto breakloop;
6039 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006040 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006041 p--;
6042 continue;
6043 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006044 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045 reject++;
6046 /* fall through */
6047 case ':':
6048 /*
6049 * sort of a hack - expand tildes in variable
6050 * assignments (after the first '=' and after ':'s).
6051 */
6052 if (*--p == '~') {
6053 goto tilde;
6054 }
6055 continue;
6056 }
6057
6058 switch (c) {
6059 case CTLENDVAR: /* ??? */
6060 goto breakloop;
6061 case CTLQUOTEMARK:
6062 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006063 if (!inquotes
6064 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006065 && ( p[4] == (char)CTLQUOTEMARK
6066 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006067 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006068 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006069 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006070 goto start;
6071 }
6072 inquotes = !inquotes;
6073 addquote:
6074 if (quotes) {
6075 p--;
6076 length++;
6077 startloc++;
6078 }
6079 break;
6080 case CTLESC:
6081 startloc++;
6082 length++;
6083 goto addquote;
6084 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006085 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006086 goto start;
6087 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006088 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006089 case CTLBACKQ|CTLQUOTE:
6090 expbackq(argbackq->n, c, quotes);
6091 argbackq = argbackq->next;
6092 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006093#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006094 case CTLENDARI:
6095 p--;
6096 expari(quotes);
6097 goto start;
6098#endif
6099 }
6100 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006101 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006102}
6103
6104static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006105scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6106 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006107{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006108 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006109 char c;
6110
6111 loc = startp;
6112 loc2 = rmesc;
6113 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006114 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006115 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006116
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006117 c = *loc2;
6118 if (zero) {
6119 *loc2 = '\0';
6120 s = rmesc;
6121 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006122 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006123
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006124 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006125 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006126 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006127 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006128 loc++;
6129 loc++;
6130 loc2++;
6131 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006132 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006133}
6134
6135static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006136scanright(char *startp, char *rmesc, char *rmescend,
6137 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006138{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006139#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6140 int try2optimize = match_at_start;
6141#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006142 int esc = 0;
6143 char *loc;
6144 char *loc2;
6145
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006146 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6147 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6148 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6149 * Logic:
6150 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6151 * and on each iteration they go back two/one char until they reach the beginning.
6152 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6153 */
6154 /* TODO: document in what other circumstances we are called. */
6155
6156 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006157 int match;
6158 char c = *loc2;
6159 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006160 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006161 *loc2 = '\0';
6162 s = rmesc;
6163 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006164 match = pmatch(pattern, s);
6165 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166 *loc2 = c;
6167 if (match)
6168 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006169#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6170 if (try2optimize) {
6171 /* Maybe we can optimize this:
6172 * if pattern ends with unescaped *, we can avoid checking
6173 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6174 * it wont match truncated "raw_value_of_" strings too.
6175 */
6176 unsigned plen = strlen(pattern);
6177 /* Does it end with "*"? */
6178 if (plen != 0 && pattern[--plen] == '*') {
6179 /* "xxxx*" is not escaped */
6180 /* "xxx\*" is escaped */
6181 /* "xx\\*" is not escaped */
6182 /* "x\\\*" is escaped */
6183 int slashes = 0;
6184 while (plen != 0 && pattern[--plen] == '\\')
6185 slashes++;
6186 if (!(slashes & 1))
6187 break; /* ends with unescaped "*" */
6188 }
6189 try2optimize = 0;
6190 }
6191#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006192 loc--;
6193 if (quotes) {
6194 if (--esc < 0) {
6195 esc = esclen(startp, loc);
6196 }
6197 if (esc % 2) {
6198 esc--;
6199 loc--;
6200 }
6201 }
6202 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006203 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006204}
6205
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006206static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006207static void
6208varunset(const char *end, const char *var, const char *umsg, int varflags)
6209{
6210 const char *msg;
6211 const char *tail;
6212
6213 tail = nullstr;
6214 msg = "parameter not set";
6215 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006216 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006217 if (varflags & VSNUL)
6218 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006219 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006220 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006221 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006223 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006224}
6225
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006226#if ENABLE_ASH_BASH_COMPAT
6227static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006228parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006229{
6230 char *idx, *repl = NULL;
6231 unsigned char c;
6232
Denys Vlasenko16149002010-08-06 22:06:21 +02006233 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006234 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006235 idx = arg;
6236 while (1) {
6237 c = *arg;
6238 if (!c)
6239 break;
6240 if (c == '/') {
6241 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006242 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006243 repl = idx + 1;
6244 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006245 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006246 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006247 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006248 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006249 /*
6250 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6251 * The result is a_\_z_c (not a\_\_z_c)!
6252 *
6253 * Enable debug prints in this function and you'll see:
6254 * ash: arg:'\\b/_\\_z_' varflags:d
6255 * ash: pattern:'\\b' repl:'_\_z_'
6256 * That is, \\b is interpreted as \\b, but \\_ as \_!
6257 * IOW: search pattern and replace string treat backslashes
6258 * differently! That is the reason why we check repl below:
6259 */
6260 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6261 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006262 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006263 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006264 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006265
6266 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006267}
6268#endif /* ENABLE_ASH_BASH_COMPAT */
6269
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006270static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006271subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006272 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006274 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006275 char *startp;
6276 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006277 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006278 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006279 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006280 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006281 int saveherefd = herefd;
6282 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006283 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006284 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006285
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006286 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6287 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006288
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006289 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006290 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6291 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006292 STPUTC('\0', expdest);
6293 herefd = saveherefd;
6294 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006295 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296
6297 switch (subtype) {
6298 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006299 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006300 amount = startp - expdest;
6301 STADJUST(amount, expdest);
6302 return startp;
6303
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006304 case VSQUESTION:
6305 varunset(p, varname, startp, varflags);
6306 /* NOTREACHED */
6307
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006308#if ENABLE_ASH_BASH_COMPAT
6309 case VSSUBSTR:
6310 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006311 /* Read POS in ${var:POS:LEN} */
6312 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006313 len = str - startp - 1;
6314
6315 /* *loc != '\0', guaranteed by parser */
6316 if (quotes) {
6317 char *ptr;
6318
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006319 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006320 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006321 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006322 len--;
6323 ptr++;
6324 }
6325 }
6326 }
6327 orig_len = len;
6328
6329 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006330 /* ${var::LEN} */
6331 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006332 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006333 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006334 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006335 while (*loc && *loc != ':') {
6336 /* TODO?
6337 * bash complains on: var=qwe; echo ${var:1a:123}
6338 if (!isdigit(*loc))
6339 ash_msg_and_raise_error(msg_illnum, str);
6340 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006341 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006342 }
6343 if (*loc++ == ':') {
6344 len = number(loc);
6345 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006346 }
6347 if (pos >= orig_len) {
6348 pos = 0;
6349 len = 0;
6350 }
6351 if (len > (orig_len - pos))
6352 len = orig_len - pos;
6353
6354 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006355 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006356 str++;
6357 }
6358 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006359 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006360 *loc++ = *str++;
6361 *loc++ = *str++;
6362 }
6363 *loc = '\0';
6364 amount = loc - expdest;
6365 STADJUST(amount, expdest);
6366 return loc;
6367#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006368 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006369
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006370 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006371
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006372 /* We'll comeback here if we grow the stack while handling
6373 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6374 * stack will need rebasing, and we'll need to remove our work
6375 * areas each time
6376 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006377 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006378
6379 amount = expdest - ((char *)stackblock() + resetloc);
6380 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006381 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006382
6383 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006384 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006385 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006386 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006387 if (rmesc != startp) {
6388 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006389 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006390 }
6391 }
6392 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006393 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006394 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006395 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006396
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006397#if ENABLE_ASH_BASH_COMPAT
6398 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006399 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006400
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006401 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006402 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006403 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006404 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006405 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006406 }
6407
6408 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006409 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006410 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006411
6412 len = 0;
6413 idx = startp;
6414 end = str - 1;
6415 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006416 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006417 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006418 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006419 if (!loc) {
6420 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006421 char *restart_detect = stackblock();
6422 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006423 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006424 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006425 idx++;
6426 len++;
6427 STPUTC(*idx, expdest);
6428 }
6429 if (stackblock() != restart_detect)
6430 goto restart;
6431 idx++;
6432 len++;
6433 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006434 /* continue; - prone to quadratic behavior, smarter code: */
6435 if (idx >= end)
6436 break;
6437 if (str[0] == '*') {
6438 /* Pattern is "*foo". If "*foo" does not match "long_string",
6439 * it would never match "ong_string" etc, no point in trying.
6440 */
6441 goto skip_matching;
6442 }
6443 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006444 }
6445
6446 if (subtype == VSREPLACEALL) {
6447 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006448 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006449 idx++;
6450 idx++;
6451 rmesc++;
6452 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006453 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006454 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006455 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006456
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006457 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006458 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006459 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006460 if (quotes && *loc == '\\') {
6461 STPUTC(CTLESC, expdest);
6462 len++;
6463 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006464 STPUTC(*loc, expdest);
6465 if (stackblock() != restart_detect)
6466 goto restart;
6467 len++;
6468 }
6469
6470 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006471 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006472 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006473 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006474 STPUTC(*idx, expdest);
6475 if (stackblock() != restart_detect)
6476 goto restart;
6477 len++;
6478 idx++;
6479 }
6480 break;
6481 }
6482 }
6483
6484 /* We've put the replaced text into a buffer at workloc, now
6485 * move it to the right place and adjust the stack.
6486 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006487 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006488 startp = (char *)stackblock() + startloc;
6489 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006490 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006491 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006492 STADJUST(-amount, expdest);
6493 return startp;
6494 }
6495#endif /* ENABLE_ASH_BASH_COMPAT */
6496
6497 subtype -= VSTRIMRIGHT;
6498#if DEBUG
6499 if (subtype < 0 || subtype > 7)
6500 abort();
6501#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006502 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006503 zero = subtype >> 1;
6504 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6505 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6506
6507 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6508 if (loc) {
6509 if (zero) {
6510 memmove(startp, loc, str - loc);
6511 loc = startp + (str - loc) - 1;
6512 }
6513 *loc = '\0';
6514 amount = loc - expdest;
6515 STADJUST(amount, expdest);
6516 }
6517 return loc;
6518}
6519
6520/*
6521 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006522 * name parameter (examples):
6523 * ash -c 'echo $1' name:'1='
6524 * ash -c 'echo $qwe' name:'qwe='
6525 * ash -c 'echo $$' name:'$='
6526 * ash -c 'echo ${$}' name:'$='
6527 * ash -c 'echo ${$##q}' name:'$=q'
6528 * ash -c 'echo ${#$}' name:'$='
6529 * note: examples with bad shell syntax:
6530 * ash -c 'echo ${#$1}' name:'$=1'
6531 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006532 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006533static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006534varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006535{
Mike Frysinger98c52642009-04-02 10:02:37 +00006536 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006537 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006538 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006539 int sepq = 0;
6540 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006541 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006542 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006543 int quoted = varflags & VSQUOTE;
6544 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006545
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006546 switch (*name) {
6547 case '$':
6548 num = rootpid;
6549 goto numvar;
6550 case '?':
6551 num = exitstatus;
6552 goto numvar;
6553 case '#':
6554 num = shellparam.nparam;
6555 goto numvar;
6556 case '!':
6557 num = backgndpid;
6558 if (num == 0)
6559 return -1;
6560 numvar:
6561 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006562 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006563 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006564 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006565 for (i = NOPTS - 1; i >= 0; i--) {
6566 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006567 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006568 len++;
6569 }
6570 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006571 check_1char_name:
6572#if 0
6573 /* handles cases similar to ${#$1} */
6574 if (name[2] != '\0')
6575 raise_error_syntax("bad substitution");
6576#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006577 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006578 case '@': {
6579 char **ap;
6580 int sep;
6581
6582 if (quoted && (flags & EXP_FULL)) {
6583 /* note: this is not meant as PEOF value */
6584 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006585 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006586 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006587 /* fall through */
6588 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006589 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006590 i = SIT(sep, syntax);
6591 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006592 sepq = 1;
6593 param:
6594 ap = shellparam.p;
6595 if (!ap)
6596 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006597 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006598 size_t partlen;
6599
6600 partlen = strlen(p);
6601 len += partlen;
6602
6603 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6604 memtodest(p, partlen, syntax, quotes);
6605
6606 if (*ap && sep) {
6607 char *q;
6608
6609 len++;
6610 if (subtype == VSPLUS || subtype == VSLENGTH) {
6611 continue;
6612 }
6613 q = expdest;
6614 if (sepq)
6615 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006616 /* note: may put NUL despite sep != 0
6617 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006618 STPUTC(sep, q);
6619 expdest = q;
6620 }
6621 }
6622 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006623 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006624 case '0':
6625 case '1':
6626 case '2':
6627 case '3':
6628 case '4':
6629 case '5':
6630 case '6':
6631 case '7':
6632 case '8':
6633 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006634 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006635 if (num < 0 || num > shellparam.nparam)
6636 return -1;
6637 p = num ? shellparam.p[num - 1] : arg0;
6638 goto value;
6639 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006640 /* NB: name has form "VAR=..." */
6641
6642 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6643 * which should be considered before we check variables. */
6644 if (var_str_list) {
6645 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6646 p = NULL;
6647 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006648 char *str, *eq;
6649 str = var_str_list->text;
6650 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006651 if (!eq) /* stop at first non-assignment */
6652 break;
6653 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006654 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006655 && strncmp(str, name, name_len) == 0
6656 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006657 p = eq;
6658 /* goto value; - WRONG! */
6659 /* think "A=1 A=2 B=$A" */
6660 }
6661 var_str_list = var_str_list->next;
6662 } while (var_str_list);
6663 if (p)
6664 goto value;
6665 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006666 p = lookupvar(name);
6667 value:
6668 if (!p)
6669 return -1;
6670
6671 len = strlen(p);
6672 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6673 memtodest(p, len, syntax, quotes);
6674 return len;
6675 }
6676
6677 if (subtype == VSPLUS || subtype == VSLENGTH)
6678 STADJUST(-len, expdest);
6679 return len;
6680}
6681
6682/*
6683 * Expand a variable, and return a pointer to the next character in the
6684 * input string.
6685 */
6686static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006687evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006688{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006689 char varflags;
6690 char subtype;
6691 char quoted;
6692 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006693 char *var;
6694 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006695 int startloc;
6696 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006697
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006698 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006699 subtype = varflags & VSTYPE;
6700 quoted = varflags & VSQUOTE;
6701 var = p;
6702 easy = (!quoted || (*var == '@' && shellparam.nparam));
6703 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006704 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006705
6706 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006707 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006708 if (varflags & VSNUL)
6709 varlen--;
6710
6711 if (subtype == VSPLUS) {
6712 varlen = -1 - varlen;
6713 goto vsplus;
6714 }
6715
6716 if (subtype == VSMINUS) {
6717 vsplus:
6718 if (varlen < 0) {
6719 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006720 p,
6721 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006722 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006723 );
6724 goto end;
6725 }
6726 if (easy)
6727 goto record;
6728 goto end;
6729 }
6730
6731 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6732 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006733 if (subevalvar(p, var, /* strloc: */ 0,
6734 subtype, startloc, varflags,
6735 /* quotes: */ 0,
6736 var_str_list)
6737 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006738 varflags &= ~VSNUL;
6739 /*
6740 * Remove any recorded regions beyond
6741 * start of variable
6742 */
6743 removerecordregions(startloc);
6744 goto again;
6745 }
6746 goto end;
6747 }
6748 if (easy)
6749 goto record;
6750 goto end;
6751 }
6752
6753 if (varlen < 0 && uflag)
6754 varunset(p, var, 0, 0);
6755
6756 if (subtype == VSLENGTH) {
6757 cvtnum(varlen > 0 ? varlen : 0);
6758 goto record;
6759 }
6760
6761 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006762 if (easy)
6763 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006764 goto end;
6765 }
6766
6767#if DEBUG
6768 switch (subtype) {
6769 case VSTRIMLEFT:
6770 case VSTRIMLEFTMAX:
6771 case VSTRIMRIGHT:
6772 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006773#if ENABLE_ASH_BASH_COMPAT
6774 case VSSUBSTR:
6775 case VSREPLACE:
6776 case VSREPLACEALL:
6777#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006778 break;
6779 default:
6780 abort();
6781 }
6782#endif
6783
6784 if (varlen >= 0) {
6785 /*
6786 * Terminate the string and start recording the pattern
6787 * right after it
6788 */
6789 STPUTC('\0', expdest);
6790 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006791 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006792 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006793//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006794 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006795 var_str_list)
6796 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006797 int amount = expdest - (
6798 (char *)stackblock() + patloc - 1
6799 );
6800 STADJUST(-amount, expdest);
6801 }
6802 /* Remove any recorded regions beyond start of variable */
6803 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006804 record:
6805 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006806 }
6807
6808 end:
6809 if (subtype != VSNORMAL) { /* skip to end of alternative */
6810 int nesting = 1;
6811 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006812 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006813 if (c == CTLESC)
6814 p++;
6815 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6816 if (varlen >= 0)
6817 argbackq = argbackq->next;
6818 } else if (c == CTLVAR) {
6819 if ((*p++ & VSTYPE) != VSNORMAL)
6820 nesting++;
6821 } else if (c == CTLENDVAR) {
6822 if (--nesting == 0)
6823 break;
6824 }
6825 }
6826 }
6827 return p;
6828}
6829
6830/*
6831 * Break the argument string into pieces based upon IFS and add the
6832 * strings to the argument list. The regions of the string to be
6833 * searched for IFS characters have been stored by recordregion.
6834 */
6835static void
6836ifsbreakup(char *string, struct arglist *arglist)
6837{
6838 struct ifsregion *ifsp;
6839 struct strlist *sp;
6840 char *start;
6841 char *p;
6842 char *q;
6843 const char *ifs, *realifs;
6844 int ifsspc;
6845 int nulonly;
6846
6847 start = string;
6848 if (ifslastp != NULL) {
6849 ifsspc = 0;
6850 nulonly = 0;
6851 realifs = ifsset() ? ifsval() : defifs;
6852 ifsp = &ifsfirst;
6853 do {
6854 p = string + ifsp->begoff;
6855 nulonly = ifsp->nulonly;
6856 ifs = nulonly ? nullstr : realifs;
6857 ifsspc = 0;
6858 while (p < string + ifsp->endoff) {
6859 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006860 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006861 p++;
6862 if (!strchr(ifs, *p)) {
6863 p++;
6864 continue;
6865 }
6866 if (!nulonly)
6867 ifsspc = (strchr(defifs, *p) != NULL);
6868 /* Ignore IFS whitespace at start */
6869 if (q == start && ifsspc) {
6870 p++;
6871 start = p;
6872 continue;
6873 }
6874 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006875 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006876 sp->text = start;
6877 *arglist->lastp = sp;
6878 arglist->lastp = &sp->next;
6879 p++;
6880 if (!nulonly) {
6881 for (;;) {
6882 if (p >= string + ifsp->endoff) {
6883 break;
6884 }
6885 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006886 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006887 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006888 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006889 p = q;
6890 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006891 }
6892 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006893 if (ifsspc) {
6894 p++;
6895 ifsspc = 0;
6896 } else {
6897 p = q;
6898 break;
6899 }
6900 } else
6901 p++;
6902 }
6903 }
6904 start = p;
6905 } /* while */
6906 ifsp = ifsp->next;
6907 } while (ifsp != NULL);
6908 if (nulonly)
6909 goto add;
6910 }
6911
6912 if (!*start)
6913 return;
6914
6915 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006916 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006917 sp->text = start;
6918 *arglist->lastp = sp;
6919 arglist->lastp = &sp->next;
6920}
6921
6922static void
6923ifsfree(void)
6924{
6925 struct ifsregion *p;
6926
6927 INT_OFF;
6928 p = ifsfirst.next;
6929 do {
6930 struct ifsregion *ifsp;
6931 ifsp = p->next;
6932 free(p);
6933 p = ifsp;
6934 } while (p);
6935 ifslastp = NULL;
6936 ifsfirst.next = NULL;
6937 INT_ON;
6938}
6939
6940/*
6941 * Add a file name to the list.
6942 */
6943static void
6944addfname(const char *name)
6945{
6946 struct strlist *sp;
6947
Denis Vlasenko597906c2008-02-20 16:38:54 +00006948 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006949 sp->text = ststrdup(name);
6950 *exparg.lastp = sp;
6951 exparg.lastp = &sp->next;
6952}
6953
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006954/*
6955 * Do metacharacter (i.e. *, ?, [...]) expansion.
6956 */
6957static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006958expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006959{
6960 char *p;
6961 const char *cp;
6962 char *start;
6963 char *endname;
6964 int metaflag;
6965 struct stat statb;
6966 DIR *dirp;
6967 struct dirent *dp;
6968 int atend;
6969 int matchdot;
6970
6971 metaflag = 0;
6972 start = name;
6973 for (p = name; *p; p++) {
6974 if (*p == '*' || *p == '?')
6975 metaflag = 1;
6976 else if (*p == '[') {
6977 char *q = p + 1;
6978 if (*q == '!')
6979 q++;
6980 for (;;) {
6981 if (*q == '\\')
6982 q++;
6983 if (*q == '/' || *q == '\0')
6984 break;
6985 if (*++q == ']') {
6986 metaflag = 1;
6987 break;
6988 }
6989 }
6990 } else if (*p == '\\')
6991 p++;
6992 else if (*p == '/') {
6993 if (metaflag)
6994 goto out;
6995 start = p + 1;
6996 }
6997 }
6998 out:
6999 if (metaflag == 0) { /* we've reached the end of the file name */
7000 if (enddir != expdir)
7001 metaflag++;
7002 p = name;
7003 do {
7004 if (*p == '\\')
7005 p++;
7006 *enddir++ = *p;
7007 } while (*p++);
7008 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7009 addfname(expdir);
7010 return;
7011 }
7012 endname = p;
7013 if (name < start) {
7014 p = name;
7015 do {
7016 if (*p == '\\')
7017 p++;
7018 *enddir++ = *p++;
7019 } while (p < start);
7020 }
7021 if (enddir == expdir) {
7022 cp = ".";
7023 } else if (enddir == expdir + 1 && *expdir == '/') {
7024 cp = "/";
7025 } else {
7026 cp = expdir;
7027 enddir[-1] = '\0';
7028 }
7029 dirp = opendir(cp);
7030 if (dirp == NULL)
7031 return;
7032 if (enddir != expdir)
7033 enddir[-1] = '/';
7034 if (*endname == 0) {
7035 atend = 1;
7036 } else {
7037 atend = 0;
7038 *endname++ = '\0';
7039 }
7040 matchdot = 0;
7041 p = start;
7042 if (*p == '\\')
7043 p++;
7044 if (*p == '.')
7045 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007046 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007047 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007048 continue;
7049 if (pmatch(start, dp->d_name)) {
7050 if (atend) {
7051 strcpy(enddir, dp->d_name);
7052 addfname(expdir);
7053 } else {
7054 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7055 continue;
7056 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007057 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007058 }
7059 }
7060 }
7061 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007062 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007063 endname[-1] = '/';
7064}
7065
7066static struct strlist *
7067msort(struct strlist *list, int len)
7068{
7069 struct strlist *p, *q = NULL;
7070 struct strlist **lpp;
7071 int half;
7072 int n;
7073
7074 if (len <= 1)
7075 return list;
7076 half = len >> 1;
7077 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007078 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007079 q = p;
7080 p = p->next;
7081 }
7082 q->next = NULL; /* terminate first half of list */
7083 q = msort(list, half); /* sort first half of list */
7084 p = msort(p, len - half); /* sort second half */
7085 lpp = &list;
7086 for (;;) {
7087#if ENABLE_LOCALE_SUPPORT
7088 if (strcoll(p->text, q->text) < 0)
7089#else
7090 if (strcmp(p->text, q->text) < 0)
7091#endif
7092 {
7093 *lpp = p;
7094 lpp = &p->next;
7095 p = *lpp;
7096 if (p == NULL) {
7097 *lpp = q;
7098 break;
7099 }
7100 } else {
7101 *lpp = q;
7102 lpp = &q->next;
7103 q = *lpp;
7104 if (q == NULL) {
7105 *lpp = p;
7106 break;
7107 }
7108 }
7109 }
7110 return list;
7111}
7112
7113/*
7114 * Sort the results of file name expansion. It calculates the number of
7115 * strings to sort and then calls msort (short for merge sort) to do the
7116 * work.
7117 */
7118static struct strlist *
7119expsort(struct strlist *str)
7120{
7121 int len;
7122 struct strlist *sp;
7123
7124 len = 0;
7125 for (sp = str; sp; sp = sp->next)
7126 len++;
7127 return msort(str, len);
7128}
7129
7130static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007131expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007132{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007133 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007134 '*', '?', '[', 0
7135 };
7136 /* TODO - EXP_REDIR */
7137
7138 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007139 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007140 struct strlist **savelastp;
7141 struct strlist *sp;
7142 char *p;
7143
7144 if (fflag)
7145 goto nometa;
7146 if (!strpbrk(str->text, metachars))
7147 goto nometa;
7148 savelastp = exparg.lastp;
7149
7150 INT_OFF;
7151 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7152 {
7153 int i = strlen(str->text);
7154 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7155 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007156 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007157 free(expdir);
7158 if (p != str->text)
7159 free(p);
7160 INT_ON;
7161 if (exparg.lastp == savelastp) {
7162 /*
7163 * no matches
7164 */
7165 nometa:
7166 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007167 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007168 exparg.lastp = &str->next;
7169 } else {
7170 *exparg.lastp = NULL;
7171 *savelastp = sp = expsort(*savelastp);
7172 while (sp->next != NULL)
7173 sp = sp->next;
7174 exparg.lastp = &sp->next;
7175 }
7176 str = str->next;
7177 }
7178}
7179
7180/*
7181 * Perform variable substitution and command substitution on an argument,
7182 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7183 * perform splitting and file name expansion. When arglist is NULL, perform
7184 * here document expansion.
7185 */
7186static void
7187expandarg(union node *arg, struct arglist *arglist, int flag)
7188{
7189 struct strlist *sp;
7190 char *p;
7191
7192 argbackq = arg->narg.backquote;
7193 STARTSTACKSTR(expdest);
7194 ifsfirst.next = NULL;
7195 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007196 argstr(arg->narg.text, flag,
7197 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007198 p = _STPUTC('\0', expdest);
7199 expdest = p - 1;
7200 if (arglist == NULL) {
7201 return; /* here document expanded */
7202 }
7203 p = grabstackstr(p);
7204 exparg.lastp = &exparg.list;
7205 /*
7206 * TODO - EXP_REDIR
7207 */
7208 if (flag & EXP_FULL) {
7209 ifsbreakup(p, &exparg);
7210 *exparg.lastp = NULL;
7211 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007212 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007213 } else {
7214 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007215 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007216 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007217 sp->text = p;
7218 *exparg.lastp = sp;
7219 exparg.lastp = &sp->next;
7220 }
7221 if (ifsfirst.next)
7222 ifsfree();
7223 *exparg.lastp = NULL;
7224 if (exparg.list) {
7225 *arglist->lastp = exparg.list;
7226 arglist->lastp = exparg.lastp;
7227 }
7228}
7229
7230/*
7231 * Expand shell variables and backquotes inside a here document.
7232 */
7233static void
7234expandhere(union node *arg, int fd)
7235{
7236 herefd = fd;
7237 expandarg(arg, (struct arglist *)NULL, 0);
7238 full_write(fd, stackblock(), expdest - (char *)stackblock());
7239}
7240
7241/*
7242 * Returns true if the pattern matches the string.
7243 */
7244static int
7245patmatch(char *pattern, const char *string)
7246{
7247 return pmatch(preglob(pattern, 0, 0), string);
7248}
7249
7250/*
7251 * See if a pattern matches in a case statement.
7252 */
7253static int
7254casematch(union node *pattern, char *val)
7255{
7256 struct stackmark smark;
7257 int result;
7258
7259 setstackmark(&smark);
7260 argbackq = pattern->narg.backquote;
7261 STARTSTACKSTR(expdest);
7262 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007263 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7264 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007265 STACKSTRNUL(expdest);
7266 result = patmatch(stackblock(), val);
7267 popstackmark(&smark);
7268 return result;
7269}
7270
7271
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007272/* ============ find_command */
7273
7274struct builtincmd {
7275 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007276 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007277 /* unsigned flags; */
7278};
7279#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007280/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007281 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007282#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007283#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007284
7285struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007286 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287 union param {
7288 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007289 /* index >= 0 for commands without path (slashes) */
7290 /* (TODO: what exactly does the value mean? PATH position?) */
7291 /* index == -1 for commands with slashes */
7292 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007293 const struct builtincmd *cmd;
7294 struct funcnode *func;
7295 } u;
7296};
7297/* values of cmdtype */
7298#define CMDUNKNOWN -1 /* no entry in table for command */
7299#define CMDNORMAL 0 /* command is an executable program */
7300#define CMDFUNCTION 1 /* command is a shell function */
7301#define CMDBUILTIN 2 /* command is a shell builtin */
7302
7303/* action to find_command() */
7304#define DO_ERR 0x01 /* prints errors */
7305#define DO_ABS 0x02 /* checks absolute paths */
7306#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7307#define DO_ALTPATH 0x08 /* using alternate path */
7308#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7309
7310static void find_command(char *, struct cmdentry *, int, const char *);
7311
7312
7313/* ============ Hashing commands */
7314
7315/*
7316 * When commands are first encountered, they are entered in a hash table.
7317 * This ensures that a full path search will not have to be done for them
7318 * on each invocation.
7319 *
7320 * We should investigate converting to a linear search, even though that
7321 * would make the command name "hash" a misnomer.
7322 */
7323
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007324struct tblentry {
7325 struct tblentry *next; /* next entry in hash chain */
7326 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007327 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007328 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007329 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007330};
7331
Denis Vlasenko01631112007-12-16 17:20:38 +00007332static struct tblentry **cmdtable;
7333#define INIT_G_cmdtable() do { \
7334 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7335} while (0)
7336
7337static int builtinloc = -1; /* index in path of %builtin, or -1 */
7338
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007339
7340static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007341tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007342{
7343 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007344
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007345#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007346 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007347 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007348 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007349 while (*envp)
7350 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007351 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007352 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007353 /* re-exec ourselves with the new arguments */
7354 execve(bb_busybox_exec_path, argv, envp);
7355 /* If they called chroot or otherwise made the binary no longer
7356 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007357 }
7358#endif
7359
7360 repeat:
7361#ifdef SYSV
7362 do {
7363 execve(cmd, argv, envp);
7364 } while (errno == EINTR);
7365#else
7366 execve(cmd, argv, envp);
7367#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007368 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007369 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007370 return;
7371 }
7372 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007373 char **ap;
7374 char **new;
7375
7376 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007377 continue;
7378 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007379 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007380 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007381 ap += 2;
7382 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007383 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007384 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007385 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007386 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007387 goto repeat;
7388 }
7389}
7390
7391/*
7392 * Exec a program. Never returns. If you change this routine, you may
7393 * have to change the find_command routine as well.
7394 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007395static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007396static void
7397shellexec(char **argv, const char *path, int idx)
7398{
7399 char *cmdname;
7400 int e;
7401 char **envp;
7402 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007403#if ENABLE_FEATURE_SH_STANDALONE
7404 int applet_no = -1;
7405#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007406
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007407 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007408 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007409 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007410#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007411 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007412#endif
7413 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007414 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007415 e = errno;
7416 } else {
7417 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007418 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007419 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007420 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007421 if (errno != ENOENT && errno != ENOTDIR)
7422 e = errno;
7423 }
7424 stunalloc(cmdname);
7425 }
7426 }
7427
7428 /* Map to POSIX errors */
7429 switch (e) {
7430 case EACCES:
7431 exerrno = 126;
7432 break;
7433 case ENOENT:
7434 exerrno = 127;
7435 break;
7436 default:
7437 exerrno = 2;
7438 break;
7439 }
7440 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007441 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7442 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007443 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7444 /* NOTREACHED */
7445}
7446
7447static void
7448printentry(struct tblentry *cmdp)
7449{
7450 int idx;
7451 const char *path;
7452 char *name;
7453
7454 idx = cmdp->param.index;
7455 path = pathval();
7456 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007457 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007458 stunalloc(name);
7459 } while (--idx >= 0);
7460 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7461}
7462
7463/*
7464 * Clear out command entries. The argument specifies the first entry in
7465 * PATH which has changed.
7466 */
7467static void
7468clearcmdentry(int firstchange)
7469{
7470 struct tblentry **tblp;
7471 struct tblentry **pp;
7472 struct tblentry *cmdp;
7473
7474 INT_OFF;
7475 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7476 pp = tblp;
7477 while ((cmdp = *pp) != NULL) {
7478 if ((cmdp->cmdtype == CMDNORMAL &&
7479 cmdp->param.index >= firstchange)
7480 || (cmdp->cmdtype == CMDBUILTIN &&
7481 builtinloc >= firstchange)
7482 ) {
7483 *pp = cmdp->next;
7484 free(cmdp);
7485 } else {
7486 pp = &cmdp->next;
7487 }
7488 }
7489 }
7490 INT_ON;
7491}
7492
7493/*
7494 * Locate a command in the command hash table. If "add" is nonzero,
7495 * add the command to the table if it is not already present. The
7496 * variable "lastcmdentry" is set to point to the address of the link
7497 * pointing to the entry, so that delete_cmd_entry can delete the
7498 * entry.
7499 *
7500 * Interrupts must be off if called with add != 0.
7501 */
7502static struct tblentry **lastcmdentry;
7503
7504static struct tblentry *
7505cmdlookup(const char *name, int add)
7506{
7507 unsigned int hashval;
7508 const char *p;
7509 struct tblentry *cmdp;
7510 struct tblentry **pp;
7511
7512 p = name;
7513 hashval = (unsigned char)*p << 4;
7514 while (*p)
7515 hashval += (unsigned char)*p++;
7516 hashval &= 0x7FFF;
7517 pp = &cmdtable[hashval % CMDTABLESIZE];
7518 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7519 if (strcmp(cmdp->cmdname, name) == 0)
7520 break;
7521 pp = &cmdp->next;
7522 }
7523 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007524 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7525 + strlen(name)
7526 /* + 1 - already done because
7527 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007528 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007529 cmdp->cmdtype = CMDUNKNOWN;
7530 strcpy(cmdp->cmdname, name);
7531 }
7532 lastcmdentry = pp;
7533 return cmdp;
7534}
7535
7536/*
7537 * Delete the command entry returned on the last lookup.
7538 */
7539static void
7540delete_cmd_entry(void)
7541{
7542 struct tblentry *cmdp;
7543
7544 INT_OFF;
7545 cmdp = *lastcmdentry;
7546 *lastcmdentry = cmdp->next;
7547 if (cmdp->cmdtype == CMDFUNCTION)
7548 freefunc(cmdp->param.func);
7549 free(cmdp);
7550 INT_ON;
7551}
7552
7553/*
7554 * Add a new command entry, replacing any existing command entry for
7555 * the same name - except special builtins.
7556 */
7557static void
7558addcmdentry(char *name, struct cmdentry *entry)
7559{
7560 struct tblentry *cmdp;
7561
7562 cmdp = cmdlookup(name, 1);
7563 if (cmdp->cmdtype == CMDFUNCTION) {
7564 freefunc(cmdp->param.func);
7565 }
7566 cmdp->cmdtype = entry->cmdtype;
7567 cmdp->param = entry->u;
7568 cmdp->rehash = 0;
7569}
7570
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007571static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007572hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007573{
7574 struct tblentry **pp;
7575 struct tblentry *cmdp;
7576 int c;
7577 struct cmdentry entry;
7578 char *name;
7579
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007580 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007581 clearcmdentry(0);
7582 return 0;
7583 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007584
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007585 if (*argptr == NULL) {
7586 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7587 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7588 if (cmdp->cmdtype == CMDNORMAL)
7589 printentry(cmdp);
7590 }
7591 }
7592 return 0;
7593 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007594
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007595 c = 0;
7596 while ((name = *argptr) != NULL) {
7597 cmdp = cmdlookup(name, 0);
7598 if (cmdp != NULL
7599 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007600 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7601 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007602 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007603 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007604 find_command(name, &entry, DO_ERR, pathval());
7605 if (entry.cmdtype == CMDUNKNOWN)
7606 c = 1;
7607 argptr++;
7608 }
7609 return c;
7610}
7611
7612/*
7613 * Called when a cd is done. Marks all commands so the next time they
7614 * are executed they will be rehashed.
7615 */
7616static void
7617hashcd(void)
7618{
7619 struct tblentry **pp;
7620 struct tblentry *cmdp;
7621
7622 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7623 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007624 if (cmdp->cmdtype == CMDNORMAL
7625 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007626 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007627 && builtinloc > 0)
7628 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007629 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007630 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007631 }
7632 }
7633}
7634
7635/*
7636 * Fix command hash table when PATH changed.
7637 * Called before PATH is changed. The argument is the new value of PATH;
7638 * pathval() still returns the old value at this point.
7639 * Called with interrupts off.
7640 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007641static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007642changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007643{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007644 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007645 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007646 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007647 int idx_bltin;
7648
7649 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007650 firstchange = 9999; /* assume no change */
7651 idx = 0;
7652 idx_bltin = -1;
7653 for (;;) {
7654 if (*old != *new) {
7655 firstchange = idx;
7656 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007657 || (*old == ':' && *new == '\0')
7658 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007659 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007660 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007661 old = new; /* ignore subsequent differences */
7662 }
7663 if (*new == '\0')
7664 break;
7665 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7666 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007667 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007668 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007669 new++;
7670 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007671 }
7672 if (builtinloc < 0 && idx_bltin >= 0)
7673 builtinloc = idx_bltin; /* zap builtins */
7674 if (builtinloc >= 0 && idx_bltin < 0)
7675 firstchange = 0;
7676 clearcmdentry(firstchange);
7677 builtinloc = idx_bltin;
7678}
7679
7680#define TEOF 0
7681#define TNL 1
7682#define TREDIR 2
7683#define TWORD 3
7684#define TSEMI 4
7685#define TBACKGND 5
7686#define TAND 6
7687#define TOR 7
7688#define TPIPE 8
7689#define TLP 9
7690#define TRP 10
7691#define TENDCASE 11
7692#define TENDBQUOTE 12
7693#define TNOT 13
7694#define TCASE 14
7695#define TDO 15
7696#define TDONE 16
7697#define TELIF 17
7698#define TELSE 18
7699#define TESAC 19
7700#define TFI 20
7701#define TFOR 21
7702#define TIF 22
7703#define TIN 23
7704#define TTHEN 24
7705#define TUNTIL 25
7706#define TWHILE 26
7707#define TBEGIN 27
7708#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007709typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007710
7711/* first char is indicating which tokens mark the end of a list */
7712static const char *const tokname_array[] = {
7713 "\1end of file",
7714 "\0newline",
7715 "\0redirection",
7716 "\0word",
7717 "\0;",
7718 "\0&",
7719 "\0&&",
7720 "\0||",
7721 "\0|",
7722 "\0(",
7723 "\1)",
7724 "\1;;",
7725 "\1`",
7726#define KWDOFFSET 13
7727 /* the following are keywords */
7728 "\0!",
7729 "\0case",
7730 "\1do",
7731 "\1done",
7732 "\1elif",
7733 "\1else",
7734 "\1esac",
7735 "\1fi",
7736 "\0for",
7737 "\0if",
7738 "\0in",
7739 "\1then",
7740 "\0until",
7741 "\0while",
7742 "\0{",
7743 "\1}",
7744};
7745
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007746/* Wrapper around strcmp for qsort/bsearch/... */
7747static int
7748pstrcmp(const void *a, const void *b)
7749{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007750 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007751}
7752
7753static const char *const *
7754findkwd(const char *s)
7755{
7756 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007757 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7758 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007759}
7760
7761/*
7762 * Locate and print what a word is...
7763 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007764static int
7765describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007766{
7767 struct cmdentry entry;
7768 struct tblentry *cmdp;
7769#if ENABLE_ASH_ALIAS
7770 const struct alias *ap;
7771#endif
7772 const char *path = pathval();
7773
7774 if (describe_command_verbose) {
7775 out1str(command);
7776 }
7777
7778 /* First look at the keywords */
7779 if (findkwd(command)) {
7780 out1str(describe_command_verbose ? " is a shell keyword" : command);
7781 goto out;
7782 }
7783
7784#if ENABLE_ASH_ALIAS
7785 /* Then look at the aliases */
7786 ap = lookupalias(command, 0);
7787 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007788 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007789 out1str("alias ");
7790 printalias(ap);
7791 return 0;
7792 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007793 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007794 goto out;
7795 }
7796#endif
7797 /* Then check if it is a tracked alias */
7798 cmdp = cmdlookup(command, 0);
7799 if (cmdp != NULL) {
7800 entry.cmdtype = cmdp->cmdtype;
7801 entry.u = cmdp->param;
7802 } else {
7803 /* Finally use brute force */
7804 find_command(command, &entry, DO_ABS, path);
7805 }
7806
7807 switch (entry.cmdtype) {
7808 case CMDNORMAL: {
7809 int j = entry.u.index;
7810 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007811 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007812 p = command;
7813 } else {
7814 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007815 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007816 stunalloc(p);
7817 } while (--j >= 0);
7818 }
7819 if (describe_command_verbose) {
7820 out1fmt(" is%s %s",
7821 (cmdp ? " a tracked alias for" : nullstr), p
7822 );
7823 } else {
7824 out1str(p);
7825 }
7826 break;
7827 }
7828
7829 case CMDFUNCTION:
7830 if (describe_command_verbose) {
7831 out1str(" is a shell function");
7832 } else {
7833 out1str(command);
7834 }
7835 break;
7836
7837 case CMDBUILTIN:
7838 if (describe_command_verbose) {
7839 out1fmt(" is a %sshell builtin",
7840 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7841 "special " : nullstr
7842 );
7843 } else {
7844 out1str(command);
7845 }
7846 break;
7847
7848 default:
7849 if (describe_command_verbose) {
7850 out1str(": not found\n");
7851 }
7852 return 127;
7853 }
7854 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007855 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007856 return 0;
7857}
7858
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007859static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007860typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007861{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007862 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007863 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007864 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007865
Denis Vlasenko46846e22007-05-20 13:08:31 +00007866 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007867 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007868 i++;
7869 verbose = 0;
7870 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007871 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007872 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007873 }
7874 return err;
7875}
7876
7877#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007878static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007879commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007880{
7881 int c;
7882 enum {
7883 VERIFY_BRIEF = 1,
7884 VERIFY_VERBOSE = 2,
7885 } verify = 0;
7886
7887 while ((c = nextopt("pvV")) != '\0')
7888 if (c == 'V')
7889 verify |= VERIFY_VERBOSE;
7890 else if (c == 'v')
7891 verify |= VERIFY_BRIEF;
7892#if DEBUG
7893 else if (c != 'p')
7894 abort();
7895#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007896 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7897 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007898 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007899 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007900
7901 return 0;
7902}
7903#endif
7904
7905
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007906/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007907
Denis Vlasenko340299a2008-11-21 10:36:36 +00007908static int funcblocksize; /* size of structures in function */
7909static int funcstringsize; /* size of strings in node */
7910static void *funcblock; /* block to allocate function from */
7911static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007912
Eric Andersencb57d552001-06-28 07:25:16 +00007913/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007914#define EV_EXIT 01 /* exit after evaluating tree */
7915#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007916#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007917
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007918static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007919 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7920 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7921 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7922 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7923 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7924 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7925 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7926 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7927 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7928 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7929 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7930 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7931 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7932 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7933 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7934 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7935 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007936#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007937 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007938#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007939 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7940 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7941 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7942 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7943 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7944 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7945 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7946 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7947 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007948};
7949
7950static void calcsize(union node *n);
7951
7952static void
7953sizenodelist(struct nodelist *lp)
7954{
7955 while (lp) {
7956 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7957 calcsize(lp->n);
7958 lp = lp->next;
7959 }
7960}
7961
7962static void
7963calcsize(union node *n)
7964{
7965 if (n == NULL)
7966 return;
7967 funcblocksize += nodesize[n->type];
7968 switch (n->type) {
7969 case NCMD:
7970 calcsize(n->ncmd.redirect);
7971 calcsize(n->ncmd.args);
7972 calcsize(n->ncmd.assign);
7973 break;
7974 case NPIPE:
7975 sizenodelist(n->npipe.cmdlist);
7976 break;
7977 case NREDIR:
7978 case NBACKGND:
7979 case NSUBSHELL:
7980 calcsize(n->nredir.redirect);
7981 calcsize(n->nredir.n);
7982 break;
7983 case NAND:
7984 case NOR:
7985 case NSEMI:
7986 case NWHILE:
7987 case NUNTIL:
7988 calcsize(n->nbinary.ch2);
7989 calcsize(n->nbinary.ch1);
7990 break;
7991 case NIF:
7992 calcsize(n->nif.elsepart);
7993 calcsize(n->nif.ifpart);
7994 calcsize(n->nif.test);
7995 break;
7996 case NFOR:
7997 funcstringsize += strlen(n->nfor.var) + 1;
7998 calcsize(n->nfor.body);
7999 calcsize(n->nfor.args);
8000 break;
8001 case NCASE:
8002 calcsize(n->ncase.cases);
8003 calcsize(n->ncase.expr);
8004 break;
8005 case NCLIST:
8006 calcsize(n->nclist.body);
8007 calcsize(n->nclist.pattern);
8008 calcsize(n->nclist.next);
8009 break;
8010 case NDEFUN:
8011 case NARG:
8012 sizenodelist(n->narg.backquote);
8013 funcstringsize += strlen(n->narg.text) + 1;
8014 calcsize(n->narg.next);
8015 break;
8016 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008017#if ENABLE_ASH_BASH_COMPAT
8018 case NTO2:
8019#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008020 case NCLOBBER:
8021 case NFROM:
8022 case NFROMTO:
8023 case NAPPEND:
8024 calcsize(n->nfile.fname);
8025 calcsize(n->nfile.next);
8026 break;
8027 case NTOFD:
8028 case NFROMFD:
8029 calcsize(n->ndup.vname);
8030 calcsize(n->ndup.next);
8031 break;
8032 case NHERE:
8033 case NXHERE:
8034 calcsize(n->nhere.doc);
8035 calcsize(n->nhere.next);
8036 break;
8037 case NNOT:
8038 calcsize(n->nnot.com);
8039 break;
8040 };
8041}
8042
8043static char *
8044nodeckstrdup(char *s)
8045{
8046 char *rtn = funcstring;
8047
8048 strcpy(funcstring, s);
8049 funcstring += strlen(s) + 1;
8050 return rtn;
8051}
8052
8053static union node *copynode(union node *);
8054
8055static struct nodelist *
8056copynodelist(struct nodelist *lp)
8057{
8058 struct nodelist *start;
8059 struct nodelist **lpp;
8060
8061 lpp = &start;
8062 while (lp) {
8063 *lpp = funcblock;
8064 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8065 (*lpp)->n = copynode(lp->n);
8066 lp = lp->next;
8067 lpp = &(*lpp)->next;
8068 }
8069 *lpp = NULL;
8070 return start;
8071}
8072
8073static union node *
8074copynode(union node *n)
8075{
8076 union node *new;
8077
8078 if (n == NULL)
8079 return NULL;
8080 new = funcblock;
8081 funcblock = (char *) funcblock + nodesize[n->type];
8082
8083 switch (n->type) {
8084 case NCMD:
8085 new->ncmd.redirect = copynode(n->ncmd.redirect);
8086 new->ncmd.args = copynode(n->ncmd.args);
8087 new->ncmd.assign = copynode(n->ncmd.assign);
8088 break;
8089 case NPIPE:
8090 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008091 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008092 break;
8093 case NREDIR:
8094 case NBACKGND:
8095 case NSUBSHELL:
8096 new->nredir.redirect = copynode(n->nredir.redirect);
8097 new->nredir.n = copynode(n->nredir.n);
8098 break;
8099 case NAND:
8100 case NOR:
8101 case NSEMI:
8102 case NWHILE:
8103 case NUNTIL:
8104 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8105 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8106 break;
8107 case NIF:
8108 new->nif.elsepart = copynode(n->nif.elsepart);
8109 new->nif.ifpart = copynode(n->nif.ifpart);
8110 new->nif.test = copynode(n->nif.test);
8111 break;
8112 case NFOR:
8113 new->nfor.var = nodeckstrdup(n->nfor.var);
8114 new->nfor.body = copynode(n->nfor.body);
8115 new->nfor.args = copynode(n->nfor.args);
8116 break;
8117 case NCASE:
8118 new->ncase.cases = copynode(n->ncase.cases);
8119 new->ncase.expr = copynode(n->ncase.expr);
8120 break;
8121 case NCLIST:
8122 new->nclist.body = copynode(n->nclist.body);
8123 new->nclist.pattern = copynode(n->nclist.pattern);
8124 new->nclist.next = copynode(n->nclist.next);
8125 break;
8126 case NDEFUN:
8127 case NARG:
8128 new->narg.backquote = copynodelist(n->narg.backquote);
8129 new->narg.text = nodeckstrdup(n->narg.text);
8130 new->narg.next = copynode(n->narg.next);
8131 break;
8132 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008133#if ENABLE_ASH_BASH_COMPAT
8134 case NTO2:
8135#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008136 case NCLOBBER:
8137 case NFROM:
8138 case NFROMTO:
8139 case NAPPEND:
8140 new->nfile.fname = copynode(n->nfile.fname);
8141 new->nfile.fd = n->nfile.fd;
8142 new->nfile.next = copynode(n->nfile.next);
8143 break;
8144 case NTOFD:
8145 case NFROMFD:
8146 new->ndup.vname = copynode(n->ndup.vname);
8147 new->ndup.dupfd = n->ndup.dupfd;
8148 new->ndup.fd = n->ndup.fd;
8149 new->ndup.next = copynode(n->ndup.next);
8150 break;
8151 case NHERE:
8152 case NXHERE:
8153 new->nhere.doc = copynode(n->nhere.doc);
8154 new->nhere.fd = n->nhere.fd;
8155 new->nhere.next = copynode(n->nhere.next);
8156 break;
8157 case NNOT:
8158 new->nnot.com = copynode(n->nnot.com);
8159 break;
8160 };
8161 new->type = n->type;
8162 return new;
8163}
8164
8165/*
8166 * Make a copy of a parse tree.
8167 */
8168static struct funcnode *
8169copyfunc(union node *n)
8170{
8171 struct funcnode *f;
8172 size_t blocksize;
8173
8174 funcblocksize = offsetof(struct funcnode, n);
8175 funcstringsize = 0;
8176 calcsize(n);
8177 blocksize = funcblocksize;
8178 f = ckmalloc(blocksize + funcstringsize);
8179 funcblock = (char *) f + offsetof(struct funcnode, n);
8180 funcstring = (char *) f + blocksize;
8181 copynode(n);
8182 f->count = 0;
8183 return f;
8184}
8185
8186/*
8187 * Define a shell function.
8188 */
8189static void
8190defun(char *name, union node *func)
8191{
8192 struct cmdentry entry;
8193
8194 INT_OFF;
8195 entry.cmdtype = CMDFUNCTION;
8196 entry.u.func = copyfunc(func);
8197 addcmdentry(name, &entry);
8198 INT_ON;
8199}
8200
Denis Vlasenko4b875702009-03-19 13:30:04 +00008201/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008202#define SKIPBREAK (1 << 0)
8203#define SKIPCONT (1 << 1)
8204#define SKIPFUNC (1 << 2)
8205#define SKIPFILE (1 << 3)
8206#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008207static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008208static int skipcount; /* number of levels to skip */
8209static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008210static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008211
Denis Vlasenko4b875702009-03-19 13:30:04 +00008212/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008213static int evalstring(char *s, int mask);
8214
Denis Vlasenko4b875702009-03-19 13:30:04 +00008215/* Called to execute a trap.
8216 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008217 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008218 *
8219 * Perhaps we should avoid entering new trap handlers
8220 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008221 */
8222static int
8223dotrap(void)
8224{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008225 uint8_t *g;
8226 int sig;
8227 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008228
8229 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008230 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008231 xbarrier();
8232
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008233 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008234 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8235 int want_exexit;
8236 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008237
Denis Vlasenko4b875702009-03-19 13:30:04 +00008238 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008239 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008240 t = trap[sig];
8241 /* non-trapped SIGINT is handled separately by raise_interrupt,
8242 * don't upset it by resetting gotsig[SIGINT-1] */
8243 if (sig == SIGINT && !t)
8244 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008245
8246 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008247 *g = 0;
8248 if (!t)
8249 continue;
8250 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008251 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008252 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008253 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008254 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008255 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008256 }
8257
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008258 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008259 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008260}
8261
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008262/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008263static void evalloop(union node *, int);
8264static void evalfor(union node *, int);
8265static void evalcase(union node *, int);
8266static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008267static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008268static void evalpipe(union node *, int);
8269static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008270static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008271static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008272
Eric Andersen62483552001-07-10 06:09:16 +00008273/*
Eric Andersenc470f442003-07-28 09:56:35 +00008274 * Evaluate a parse tree. The value is left in the global variable
8275 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008276 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008277static void
Eric Andersenc470f442003-07-28 09:56:35 +00008278evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008279{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008280 struct jmploc *volatile savehandler = exception_handler;
8281 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008282 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008283 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008284 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008285 int int_level;
8286
8287 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008288
Eric Andersenc470f442003-07-28 09:56:35 +00008289 if (n == NULL) {
8290 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008291 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008292 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008293 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008294
8295 exception_handler = &jmploc;
8296 {
8297 int err = setjmp(jmploc.loc);
8298 if (err) {
8299 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008300 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008301 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8302 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008303 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008304 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008305 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008306 TRACE(("exception %d in evaltree, propagating err=%d\n",
8307 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008308 exception_handler = savehandler;
8309 longjmp(exception_handler->loc, err);
8310 }
8311 }
8312
Eric Andersenc470f442003-07-28 09:56:35 +00008313 switch (n->type) {
8314 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008315#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008316 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008317 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008318 break;
8319#endif
8320 case NNOT:
8321 evaltree(n->nnot.com, EV_TESTED);
8322 status = !exitstatus;
8323 goto setstatus;
8324 case NREDIR:
8325 expredir(n->nredir.redirect);
8326 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8327 if (!status) {
8328 evaltree(n->nredir.n, flags & EV_TESTED);
8329 status = exitstatus;
8330 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008331 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008332 goto setstatus;
8333 case NCMD:
8334 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008335 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008336 if (eflag && !(flags & EV_TESTED))
8337 checkexit = ~0;
8338 goto calleval;
8339 case NFOR:
8340 evalfn = evalfor;
8341 goto calleval;
8342 case NWHILE:
8343 case NUNTIL:
8344 evalfn = evalloop;
8345 goto calleval;
8346 case NSUBSHELL:
8347 case NBACKGND:
8348 evalfn = evalsubshell;
8349 goto calleval;
8350 case NPIPE:
8351 evalfn = evalpipe;
8352 goto checkexit;
8353 case NCASE:
8354 evalfn = evalcase;
8355 goto calleval;
8356 case NAND:
8357 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008358 case NSEMI: {
8359
Eric Andersenc470f442003-07-28 09:56:35 +00008360#if NAND + 1 != NOR
8361#error NAND + 1 != NOR
8362#endif
8363#if NOR + 1 != NSEMI
8364#error NOR + 1 != NSEMI
8365#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008366 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008367 evaltree(
8368 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008369 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008370 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008371 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008372 break;
8373 if (!evalskip) {
8374 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008375 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008376 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008377 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008378 evalfn(n, flags);
8379 break;
8380 }
8381 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008382 }
Eric Andersenc470f442003-07-28 09:56:35 +00008383 case NIF:
8384 evaltree(n->nif.test, EV_TESTED);
8385 if (evalskip)
8386 break;
8387 if (exitstatus == 0) {
8388 n = n->nif.ifpart;
8389 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008390 }
8391 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008392 n = n->nif.elsepart;
8393 goto evaln;
8394 }
8395 goto success;
8396 case NDEFUN:
8397 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008398 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008399 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008400 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008401 exitstatus = status;
8402 break;
8403 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008404
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008405 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008406 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008407
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008408 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008409 /* Order of checks below is important:
8410 * signal handlers trigger before exit caused by "set -e".
8411 */
8412 if (pending_sig && dotrap())
8413 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008414 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008415 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008416
8417 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008418 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008419 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008420 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008421
8422 RESTORE_INT(int_level);
8423 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008424}
8425
Eric Andersenc470f442003-07-28 09:56:35 +00008426#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8427static
8428#endif
8429void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8430
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008431static void
Eric Andersenc470f442003-07-28 09:56:35 +00008432evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008433{
8434 int status;
8435
8436 loopnest++;
8437 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008438 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008439 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008440 int i;
8441
Eric Andersencb57d552001-06-28 07:25:16 +00008442 evaltree(n->nbinary.ch1, EV_TESTED);
8443 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008444 skipping:
8445 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008446 evalskip = 0;
8447 continue;
8448 }
8449 if (evalskip == SKIPBREAK && --skipcount <= 0)
8450 evalskip = 0;
8451 break;
8452 }
Eric Andersenc470f442003-07-28 09:56:35 +00008453 i = exitstatus;
8454 if (n->type != NWHILE)
8455 i = !i;
8456 if (i != 0)
8457 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008458 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008459 status = exitstatus;
8460 if (evalskip)
8461 goto skipping;
8462 }
8463 loopnest--;
8464 exitstatus = status;
8465}
8466
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008467static void
Eric Andersenc470f442003-07-28 09:56:35 +00008468evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008469{
8470 struct arglist arglist;
8471 union node *argp;
8472 struct strlist *sp;
8473 struct stackmark smark;
8474
8475 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008476 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008477 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008478 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008479 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008480 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008481 if (evalskip)
8482 goto out;
8483 }
8484 *arglist.lastp = NULL;
8485
8486 exitstatus = 0;
8487 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008488 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008489 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008490 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008491 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008492 if (evalskip) {
8493 if (evalskip == SKIPCONT && --skipcount <= 0) {
8494 evalskip = 0;
8495 continue;
8496 }
8497 if (evalskip == SKIPBREAK && --skipcount <= 0)
8498 evalskip = 0;
8499 break;
8500 }
8501 }
8502 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008503 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008504 popstackmark(&smark);
8505}
8506
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008507static void
Eric Andersenc470f442003-07-28 09:56:35 +00008508evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008509{
8510 union node *cp;
8511 union node *patp;
8512 struct arglist arglist;
8513 struct stackmark smark;
8514
8515 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008516 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008517 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008518 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008519 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008520 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8521 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008522 if (casematch(patp, arglist.list->text)) {
8523 if (evalskip == 0) {
8524 evaltree(cp->nclist.body, flags);
8525 }
8526 goto out;
8527 }
8528 }
8529 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008530 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008531 popstackmark(&smark);
8532}
8533
Eric Andersenc470f442003-07-28 09:56:35 +00008534/*
8535 * Kick off a subshell to evaluate a tree.
8536 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008537static void
Eric Andersenc470f442003-07-28 09:56:35 +00008538evalsubshell(union node *n, int flags)
8539{
8540 struct job *jp;
8541 int backgnd = (n->type == NBACKGND);
8542 int status;
8543
8544 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008545 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008546 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008547 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008548 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008549 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008550 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008551 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008552 flags |= EV_EXIT;
8553 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008554 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008555 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008556 redirect(n->nredir.redirect, 0);
8557 evaltreenr(n->nredir.n, flags);
8558 /* never returns */
8559 }
8560 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008561 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008562 status = waitforjob(jp);
8563 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008564 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008565}
8566
Eric Andersenc470f442003-07-28 09:56:35 +00008567/*
8568 * Compute the names of the files in a redirection list.
8569 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008570static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008571static void
8572expredir(union node *n)
8573{
8574 union node *redir;
8575
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008576 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008577 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008578
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008579 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008580 fn.lastp = &fn.list;
8581 switch (redir->type) {
8582 case NFROMTO:
8583 case NFROM:
8584 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008585#if ENABLE_ASH_BASH_COMPAT
8586 case NTO2:
8587#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008588 case NCLOBBER:
8589 case NAPPEND:
8590 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008591#if ENABLE_ASH_BASH_COMPAT
8592 store_expfname:
8593#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008594 redir->nfile.expfname = fn.list->text;
8595 break;
8596 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008597 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008598 if (redir->ndup.vname) {
8599 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008600 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008601 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008602#if ENABLE_ASH_BASH_COMPAT
8603//FIXME: we used expandarg with different args!
8604 if (!isdigit_str9(fn.list->text)) {
8605 /* >&file, not >&fd */
8606 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8607 ash_msg_and_raise_error("redir error");
8608 redir->type = NTO2;
8609 goto store_expfname;
8610 }
8611#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008612 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008613 }
8614 break;
8615 }
8616 }
8617}
8618
Eric Andersencb57d552001-06-28 07:25:16 +00008619/*
Eric Andersencb57d552001-06-28 07:25:16 +00008620 * Evaluate a pipeline. All the processes in the pipeline are children
8621 * of the process creating the pipeline. (This differs from some versions
8622 * of the shell, which make the last process in a pipeline the parent
8623 * of all the rest.)
8624 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008625static void
Eric Andersenc470f442003-07-28 09:56:35 +00008626evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008627{
8628 struct job *jp;
8629 struct nodelist *lp;
8630 int pipelen;
8631 int prevfd;
8632 int pip[2];
8633
Eric Andersenc470f442003-07-28 09:56:35 +00008634 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008635 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008636 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008637 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008638 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008639 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008640 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008641 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008642 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008643 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008644 pip[1] = -1;
8645 if (lp->next) {
8646 if (pipe(pip) < 0) {
8647 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008648 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008649 }
8650 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008651 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008652 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008653 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008654 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008655 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008656 if (prevfd > 0) {
8657 dup2(prevfd, 0);
8658 close(prevfd);
8659 }
8660 if (pip[1] > 1) {
8661 dup2(pip[1], 1);
8662 close(pip[1]);
8663 }
Eric Andersenc470f442003-07-28 09:56:35 +00008664 evaltreenr(lp->n, flags);
8665 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008666 }
8667 if (prevfd >= 0)
8668 close(prevfd);
8669 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008670 /* Don't want to trigger debugging */
8671 if (pip[1] != -1)
8672 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008673 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008674 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008675 exitstatus = waitforjob(jp);
8676 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008677 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008678 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008679}
8680
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008681/*
8682 * Controls whether the shell is interactive or not.
8683 */
8684static void
8685setinteractive(int on)
8686{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008687 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008688
8689 if (++on == is_interactive)
8690 return;
8691 is_interactive = on;
8692 setsignal(SIGINT);
8693 setsignal(SIGQUIT);
8694 setsignal(SIGTERM);
8695#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8696 if (is_interactive > 1) {
8697 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008698 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008699
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008700 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008701 /* note: ash and hush share this string */
8702 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008703 "Enter 'help' for a list of built-in commands."
8704 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008705 bb_banner,
8706 "built-in shell (ash)"
8707 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008708 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008709 }
8710 }
8711#endif
8712}
8713
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008714static void
8715optschanged(void)
8716{
8717#if DEBUG
8718 opentrace();
8719#endif
8720 setinteractive(iflag);
8721 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008722#if ENABLE_FEATURE_EDITING_VI
8723 if (viflag)
8724 line_input_state->flags |= VI_MODE;
8725 else
8726 line_input_state->flags &= ~VI_MODE;
8727#else
8728 viflag = 0; /* forcibly keep the option off */
8729#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008730}
8731
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008732static struct localvar *localvars;
8733
8734/*
8735 * Called after a function returns.
8736 * Interrupts must be off.
8737 */
8738static void
8739poplocalvars(void)
8740{
8741 struct localvar *lvp;
8742 struct var *vp;
8743
8744 while ((lvp = localvars) != NULL) {
8745 localvars = lvp->next;
8746 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008747 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008748 if (vp == NULL) { /* $- saved */
8749 memcpy(optlist, lvp->text, sizeof(optlist));
8750 free((char*)lvp->text);
8751 optschanged();
8752 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008753 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008754 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008755 if (vp->var_func)
8756 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008757 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008758 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008759 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008760 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008761 }
8762 free(lvp);
8763 }
8764}
8765
8766static int
8767evalfun(struct funcnode *func, int argc, char **argv, int flags)
8768{
8769 volatile struct shparam saveparam;
8770 struct localvar *volatile savelocalvars;
8771 struct jmploc *volatile savehandler;
8772 struct jmploc jmploc;
8773 int e;
8774
8775 saveparam = shellparam;
8776 savelocalvars = localvars;
8777 e = setjmp(jmploc.loc);
8778 if (e) {
8779 goto funcdone;
8780 }
8781 INT_OFF;
8782 savehandler = exception_handler;
8783 exception_handler = &jmploc;
8784 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008785 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008786 func->count++;
8787 funcnest++;
8788 INT_ON;
8789 shellparam.nparam = argc - 1;
8790 shellparam.p = argv + 1;
8791#if ENABLE_ASH_GETOPTS
8792 shellparam.optind = 1;
8793 shellparam.optoff = -1;
8794#endif
8795 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008796 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008797 INT_OFF;
8798 funcnest--;
8799 freefunc(func);
8800 poplocalvars();
8801 localvars = savelocalvars;
8802 freeparam(&shellparam);
8803 shellparam = saveparam;
8804 exception_handler = savehandler;
8805 INT_ON;
8806 evalskip &= ~SKIPFUNC;
8807 return e;
8808}
8809
Denis Vlasenko131ae172007-02-18 13:00:19 +00008810#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008811static char **
8812parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008813{
8814 char *cp, c;
8815
8816 for (;;) {
8817 cp = *++argv;
8818 if (!cp)
8819 return 0;
8820 if (*cp++ != '-')
8821 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008822 c = *cp++;
8823 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008824 break;
8825 if (c == '-' && !*cp) {
8826 argv++;
8827 break;
8828 }
8829 do {
8830 switch (c) {
8831 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008832 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008833 break;
8834 default:
8835 /* run 'typecmd' for other options */
8836 return 0;
8837 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008838 c = *cp++;
8839 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008840 }
8841 return argv;
8842}
8843#endif
8844
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008845/*
8846 * Make a variable a local variable. When a variable is made local, it's
8847 * value and flags are saved in a localvar structure. The saved values
8848 * will be restored when the shell function returns. We handle the name
8849 * "-" as a special case.
8850 */
8851static void
8852mklocal(char *name)
8853{
8854 struct localvar *lvp;
8855 struct var **vpp;
8856 struct var *vp;
8857
8858 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008859 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008860 if (LONE_DASH(name)) {
8861 char *p;
8862 p = ckmalloc(sizeof(optlist));
8863 lvp->text = memcpy(p, optlist, sizeof(optlist));
8864 vp = NULL;
8865 } else {
8866 char *eq;
8867
8868 vpp = hashvar(name);
8869 vp = *findvar(vpp, name);
8870 eq = strchr(name, '=');
8871 if (vp == NULL) {
8872 if (eq)
8873 setvareq(name, VSTRFIXED);
8874 else
8875 setvar(name, NULL, VSTRFIXED);
8876 vp = *vpp; /* the new variable */
8877 lvp->flags = VUNSET;
8878 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008879 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008880 lvp->flags = vp->flags;
8881 vp->flags |= VSTRFIXED|VTEXTFIXED;
8882 if (eq)
8883 setvareq(name, 0);
8884 }
8885 }
8886 lvp->vp = vp;
8887 lvp->next = localvars;
8888 localvars = lvp;
8889 INT_ON;
8890}
8891
8892/*
8893 * The "local" command.
8894 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008895static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008896localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008897{
8898 char *name;
8899
8900 argv = argptr;
8901 while ((name = *argv++) != NULL) {
8902 mklocal(name);
8903 }
8904 return 0;
8905}
8906
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008907static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008908falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008909{
8910 return 1;
8911}
8912
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008913static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008914truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008915{
8916 return 0;
8917}
8918
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008919static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008920execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008921{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008922 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008923 iflag = 0; /* exit on error */
8924 mflag = 0;
8925 optschanged();
8926 shellexec(argv + 1, pathval(), 0);
8927 }
8928 return 0;
8929}
8930
8931/*
8932 * The return command.
8933 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008934static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008935returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008936{
8937 /*
8938 * If called outside a function, do what ksh does;
8939 * skip the rest of the file.
8940 */
8941 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8942 return argv[1] ? number(argv[1]) : exitstatus;
8943}
8944
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008945/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008946static int breakcmd(int, char **) FAST_FUNC;
8947static int dotcmd(int, char **) FAST_FUNC;
8948static int evalcmd(int, char **) FAST_FUNC;
8949static int exitcmd(int, char **) FAST_FUNC;
8950static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008951#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008952static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008953#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008954#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008955static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00008956#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00008957#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008958static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008959#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008960static int readcmd(int, char **) FAST_FUNC;
8961static int setcmd(int, char **) FAST_FUNC;
8962static int shiftcmd(int, char **) FAST_FUNC;
8963static int timescmd(int, char **) FAST_FUNC;
8964static int trapcmd(int, char **) FAST_FUNC;
8965static int umaskcmd(int, char **) FAST_FUNC;
8966static int unsetcmd(int, char **) FAST_FUNC;
8967static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008968
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008969#define BUILTIN_NOSPEC "0"
8970#define BUILTIN_SPECIAL "1"
8971#define BUILTIN_REGULAR "2"
8972#define BUILTIN_SPEC_REG "3"
8973#define BUILTIN_ASSIGN "4"
8974#define BUILTIN_SPEC_ASSG "5"
8975#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008976#define BUILTIN_SPEC_REG_ASSG "7"
8977
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008978/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008979#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008980static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008981#endif
8982#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008983static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008984#endif
8985#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008986static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008987#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00008988
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008989/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008990static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01008991 { BUILTIN_SPEC_REG "." , dotcmd },
8992 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008993#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01008994 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008995#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01008996 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008997#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008998#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008999#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009000 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009001#endif
9002#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009003 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009004#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009005 { BUILTIN_SPEC_REG "break" , breakcmd },
9006 { BUILTIN_REGULAR "cd" , cdcmd },
9007 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009008#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009009 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009010#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009011 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009012#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009013 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009014#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009015 { BUILTIN_SPEC_REG "eval" , evalcmd },
9016 { BUILTIN_SPEC_REG "exec" , execcmd },
9017 { BUILTIN_SPEC_REG "exit" , exitcmd },
9018 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9019 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009020#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009021 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009022#endif
9023#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009024 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009025#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009026 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009027#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009028 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009029#endif
9030#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009031 { BUILTIN_REGULAR "jobs" , jobscmd },
9032 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009033#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009034#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009035 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009036#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009037 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009038#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009039 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009040#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009041 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9042 { BUILTIN_REGULAR "read" , readcmd },
9043 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9044 { BUILTIN_SPEC_REG "return" , returncmd },
9045 { BUILTIN_SPEC_REG "set" , setcmd },
9046 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009047#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009048 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009049#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009050#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009051 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009052#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009053 { BUILTIN_SPEC_REG "times" , timescmd },
9054 { BUILTIN_SPEC_REG "trap" , trapcmd },
9055 { BUILTIN_REGULAR "true" , truecmd },
9056 { BUILTIN_NOSPEC "type" , typecmd },
9057 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9058 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009059#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009060 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009061#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009062 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9063 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009064};
9065
Denis Vlasenko80591b02008-03-25 07:49:43 +00009066/* Should match the above table! */
9067#define COMMANDCMD (builtintab + \
9068 2 + \
9069 1 * ENABLE_ASH_BUILTIN_TEST + \
9070 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9071 1 * ENABLE_ASH_ALIAS + \
9072 1 * ENABLE_ASH_JOB_CONTROL + \
9073 3)
9074#define EXECCMD (builtintab + \
9075 2 + \
9076 1 * ENABLE_ASH_BUILTIN_TEST + \
9077 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9078 1 * ENABLE_ASH_ALIAS + \
9079 1 * ENABLE_ASH_JOB_CONTROL + \
9080 3 + \
9081 1 * ENABLE_ASH_CMDCMD + \
9082 1 + \
9083 ENABLE_ASH_BUILTIN_ECHO + \
9084 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009085
9086/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009087 * Search the table of builtin commands.
9088 */
9089static struct builtincmd *
9090find_builtin(const char *name)
9091{
9092 struct builtincmd *bp;
9093
9094 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009095 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009096 pstrcmp
9097 );
9098 return bp;
9099}
9100
9101/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009102 * Execute a simple command.
9103 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009104static int
9105isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009106{
9107 const char *q = endofname(p);
9108 if (p == q)
9109 return 0;
9110 return *q == '=';
9111}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009112static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009113bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009114{
9115 /* Preserve exitstatus of a previous possible redirection
9116 * as POSIX mandates */
9117 return back_exitstatus;
9118}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009119static void
Eric Andersenc470f442003-07-28 09:56:35 +00009120evalcommand(union node *cmd, int flags)
9121{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009122 static const struct builtincmd null_bltin = {
9123 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009124 };
Eric Andersenc470f442003-07-28 09:56:35 +00009125 struct stackmark smark;
9126 union node *argp;
9127 struct arglist arglist;
9128 struct arglist varlist;
9129 char **argv;
9130 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009131 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009132 struct cmdentry cmdentry;
9133 struct job *jp;
9134 char *lastarg;
9135 const char *path;
9136 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009137 int status;
9138 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009139 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009140 smallint cmd_is_exec;
9141 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009142
9143 /* First expand the arguments. */
9144 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9145 setstackmark(&smark);
9146 back_exitstatus = 0;
9147
9148 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009149 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009150 varlist.lastp = &varlist.list;
9151 *varlist.lastp = NULL;
9152 arglist.lastp = &arglist.list;
9153 *arglist.lastp = NULL;
9154
9155 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009156 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009157 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9158 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9159 }
9160
Eric Andersenc470f442003-07-28 09:56:35 +00009161 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9162 struct strlist **spp;
9163
9164 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009165 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009166 expandarg(argp, &arglist, EXP_VARTILDE);
9167 else
9168 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9169
Eric Andersenc470f442003-07-28 09:56:35 +00009170 for (sp = *spp; sp; sp = sp->next)
9171 argc++;
9172 }
9173
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009174 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009175 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009176 TRACE(("evalcommand arg: %s\n", sp->text));
9177 *nargv++ = sp->text;
9178 }
9179 *nargv = NULL;
9180
9181 lastarg = NULL;
9182 if (iflag && funcnest == 0 && argc > 0)
9183 lastarg = nargv[-1];
9184
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009185 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009186 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009187 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009188
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009189 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009190 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9191 struct strlist **spp;
9192 char *p;
9193
9194 spp = varlist.lastp;
9195 expandarg(argp, &varlist, EXP_VARTILDE);
9196
9197 /*
9198 * Modify the command lookup path, if a PATH= assignment
9199 * is present
9200 */
9201 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009202 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009203 path = p;
9204 }
9205
9206 /* Print the command if xflag is set. */
9207 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009208 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009209 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009210
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009211 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009212 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009213 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009214 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009215 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009216 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009217 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009218 }
9219 sp = arglist.list;
9220 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009221 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009222 }
9223
9224 cmd_is_exec = 0;
9225 spclbltin = -1;
9226
9227 /* Now locate the command. */
9228 if (argc) {
9229 const char *oldpath;
9230 int cmd_flag = DO_ERR;
9231
9232 path += 5;
9233 oldpath = path;
9234 for (;;) {
9235 find_command(argv[0], &cmdentry, cmd_flag, path);
9236 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009237 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009238 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009239 goto bail;
9240 }
9241
9242 /* implement bltin and command here */
9243 if (cmdentry.cmdtype != CMDBUILTIN)
9244 break;
9245 if (spclbltin < 0)
9246 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9247 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009248 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009249#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009250 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009251 path = oldpath;
9252 nargv = parse_command_args(argv, &path);
9253 if (!nargv)
9254 break;
9255 argc -= nargv - argv;
9256 argv = nargv;
9257 cmd_flag |= DO_NOFUNC;
9258 } else
9259#endif
9260 break;
9261 }
9262 }
9263
9264 if (status) {
9265 /* We have a redirection error. */
9266 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009267 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009268 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009269 exitstatus = status;
9270 goto out;
9271 }
9272
9273 /* Execute the command. */
9274 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009275 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009276
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009277#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009278/* (1) BUG: if variables are set, we need to fork, or save/restore them
9279 * around run_nofork_applet() call.
9280 * (2) Should this check also be done in forkshell()?
9281 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9282 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009283 /* find_command() encodes applet_no as (-2 - applet_no) */
9284 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009285 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009286 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009287 /* run <applet>_main() */
9288 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009289 break;
9290 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009291#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009292 /* Can we avoid forking off? For example, very last command
9293 * in a script or a subshell does not need forking,
9294 * we can just exec it.
9295 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009296 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009297 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009298 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009299 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009300 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009301 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009302 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009303 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009304 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009305 break;
9306 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009307 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009308 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009309 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009310 }
9311 listsetvar(varlist.list, VEXPORT|VSTACK);
9312 shellexec(argv, path, cmdentry.u.index);
9313 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009314 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009315 case CMDBUILTIN:
9316 cmdenviron = varlist.list;
9317 if (cmdenviron) {
9318 struct strlist *list = cmdenviron;
9319 int i = VNOSET;
9320 if (spclbltin > 0 || argc == 0) {
9321 i = 0;
9322 if (cmd_is_exec && argc > 1)
9323 i = VEXPORT;
9324 }
9325 listsetvar(list, i);
9326 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009327 /* Tight loop with builtins only:
9328 * "while kill -0 $child; do true; done"
9329 * will never exit even if $child died, unless we do this
9330 * to reap the zombie and make kill detect that it's gone: */
9331 dowait(DOWAIT_NONBLOCK, NULL);
9332
Eric Andersenc470f442003-07-28 09:56:35 +00009333 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9334 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009335 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009336 if (i == EXEXIT)
9337 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009338 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009339 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009340 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009341 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009342 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009343 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009344 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009345 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009346 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009347 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009348 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009349 }
9350 break;
9351
9352 case CMDFUNCTION:
9353 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009354 /* See above for the rationale */
9355 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009356 if (evalfun(cmdentry.u.func, argc, argv, flags))
9357 goto raise;
9358 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009359
9360 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009361
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009362 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009363 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009364 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009365 /* dsl: I think this is intended to be used to support
9366 * '_' in 'vi' command mode during line editing...
9367 * However I implemented that within libedit itself.
9368 */
9369 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009370 }
Eric Andersenc470f442003-07-28 09:56:35 +00009371 popstackmark(&smark);
9372}
9373
9374static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009375evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9376{
Eric Andersenc470f442003-07-28 09:56:35 +00009377 char *volatile savecmdname;
9378 struct jmploc *volatile savehandler;
9379 struct jmploc jmploc;
9380 int i;
9381
9382 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009383 i = setjmp(jmploc.loc);
9384 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009385 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009386 savehandler = exception_handler;
9387 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009388 commandname = argv[0];
9389 argptr = argv + 1;
9390 optptr = NULL; /* initialize nextopt */
9391 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009392 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009393 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009394 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009395 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009396 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009397 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009398
9399 return i;
9400}
9401
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009402static int
9403goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009404{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009405 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009406}
9407
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009408
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009409/*
9410 * Search for a command. This is called before we fork so that the
9411 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009412 * the child. The check for "goodname" is an overly conservative
9413 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009414 */
Eric Andersenc470f442003-07-28 09:56:35 +00009415static void
9416prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009417{
9418 struct cmdentry entry;
9419
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009420 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9421 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009422}
9423
Eric Andersencb57d552001-06-28 07:25:16 +00009424
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009425/* ============ Builtin commands
9426 *
9427 * Builtin commands whose functions are closely tied to evaluation
9428 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009429 */
9430
9431/*
Eric Andersencb57d552001-06-28 07:25:16 +00009432 * Handle break and continue commands. Break, continue, and return are
9433 * all handled by setting the evalskip flag. The evaluation routines
9434 * above all check this flag, and if it is set they start skipping
9435 * commands rather than executing them. The variable skipcount is
9436 * the number of loops to break/continue, or the number of function
9437 * levels to return. (The latter is always 1.) It should probably
9438 * be an error to break out of more loops than exist, but it isn't
9439 * in the standard shell so we don't make it one here.
9440 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009441static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009442breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009443{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009444 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009445
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009446 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009447 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009448 if (n > loopnest)
9449 n = loopnest;
9450 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009451 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009452 skipcount = n;
9453 }
9454 return 0;
9455}
9456
Eric Andersenc470f442003-07-28 09:56:35 +00009457
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009458/* ============ input.c
9459 *
Eric Andersen90898442003-08-06 11:20:52 +00009460 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009461 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009462
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009463enum {
9464 INPUT_PUSH_FILE = 1,
9465 INPUT_NOFILE_OK = 2,
9466};
Eric Andersencb57d552001-06-28 07:25:16 +00009467
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009468static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009469/* values of checkkwd variable */
9470#define CHKALIAS 0x1
9471#define CHKKWD 0x2
9472#define CHKNL 0x4
9473
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009474/*
9475 * Push a string back onto the input at this current parsefile level.
9476 * We handle aliases this way.
9477 */
9478#if !ENABLE_ASH_ALIAS
9479#define pushstring(s, ap) pushstring(s)
9480#endif
9481static void
9482pushstring(char *s, struct alias *ap)
9483{
9484 struct strpush *sp;
9485 int len;
9486
9487 len = strlen(s);
9488 INT_OFF;
9489 if (g_parsefile->strpush) {
9490 sp = ckzalloc(sizeof(*sp));
9491 sp->prev = g_parsefile->strpush;
9492 } else {
9493 sp = &(g_parsefile->basestrpush);
9494 }
9495 g_parsefile->strpush = sp;
9496 sp->prev_string = g_parsefile->next_to_pgetc;
9497 sp->prev_left_in_line = g_parsefile->left_in_line;
9498#if ENABLE_ASH_ALIAS
9499 sp->ap = ap;
9500 if (ap) {
9501 ap->flag |= ALIASINUSE;
9502 sp->string = s;
9503 }
9504#endif
9505 g_parsefile->next_to_pgetc = s;
9506 g_parsefile->left_in_line = len;
9507 INT_ON;
9508}
9509
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009510static void
9511popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009512{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009513 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009514
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009515 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009516#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009517 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009518 if (g_parsefile->next_to_pgetc[-1] == ' '
9519 || g_parsefile->next_to_pgetc[-1] == '\t'
9520 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009521 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009522 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009523 if (sp->string != sp->ap->val) {
9524 free(sp->string);
9525 }
9526 sp->ap->flag &= ~ALIASINUSE;
9527 if (sp->ap->flag & ALIASDEAD) {
9528 unalias(sp->ap->name);
9529 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009530 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009531#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009532 g_parsefile->next_to_pgetc = sp->prev_string;
9533 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009534 g_parsefile->strpush = sp->prev;
9535 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009536 free(sp);
9537 INT_ON;
9538}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009539
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009540//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9541//it peeks whether it is &>, and then pushes back both chars.
9542//This function needs to save last *next_to_pgetc to buf[0]
9543//to make two pungetc() reliable. Currently,
9544// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009545static int
9546preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009547{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009548 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009549 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009550
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009551 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009552#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009553 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009554 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9555 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009556 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009557#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009558 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009559#endif
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02009560 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009561 if (nr == 0) {
9562 /* Ctrl+C pressed */
9563 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009564 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009565 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009566 raise(SIGINT);
9567 return 1;
9568 }
Eric Andersenc470f442003-07-28 09:56:35 +00009569 goto retry;
9570 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009571 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009572 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009573 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009574 }
Eric Andersencb57d552001-06-28 07:25:16 +00009575 }
9576#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009577 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009578#endif
9579
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009580#if 0
9581/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009582 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009583 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009584 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009585 if (flags >= 0 && (flags & O_NONBLOCK)) {
9586 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009587 if (fcntl(0, F_SETFL, flags) >= 0) {
9588 out2str("sh: turning off NDELAY mode\n");
9589 goto retry;
9590 }
9591 }
9592 }
9593 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009594#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009595 return nr;
9596}
9597
9598/*
9599 * Refill the input buffer and return the next input character:
9600 *
9601 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009602 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9603 * or we are reading from a string so we can't refill the buffer,
9604 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009605 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009606 * 4) Process input up to the next newline, deleting nul characters.
9607 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009608//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9609#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009610static int
Eric Andersenc470f442003-07-28 09:56:35 +00009611preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009612{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009613 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009614 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009615
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009616 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009617#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009618 if (g_parsefile->left_in_line == -1
9619 && g_parsefile->strpush->ap
9620 && g_parsefile->next_to_pgetc[-1] != ' '
9621 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009622 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009623 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009624 return PEOA;
9625 }
Eric Andersen2870d962001-07-02 17:27:21 +00009626#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009627 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009628 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009629 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9630 g_parsefile->left_in_line,
9631 g_parsefile->next_to_pgetc,
9632 g_parsefile->next_to_pgetc);
9633 if (--g_parsefile->left_in_line >= 0)
9634 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009635 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009636 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009637 * "pgetc" needs refilling.
9638 */
9639
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009640 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009641 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009642 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009643 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009644 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009645 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009646 /* even in failure keep left_in_line and next_to_pgetc
9647 * in lock step, for correct multi-layer pungetc.
9648 * left_in_line was decremented before preadbuffer(),
9649 * must inc next_to_pgetc: */
9650 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009651 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009652 }
Eric Andersencb57d552001-06-28 07:25:16 +00009653
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009654 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009655 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009656 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009657 again:
9658 more = preadfd();
9659 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009660 /* don't try reading again */
9661 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009662 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009663 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009664 return PEOF;
9665 }
9666 }
9667
Denis Vlasenko727752d2008-11-28 03:41:47 +00009668 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009669 * Set g_parsefile->left_in_line
9670 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009671 * NUL chars are deleted.
9672 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009673 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009674 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009675 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009676
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009677 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009678
Denis Vlasenko727752d2008-11-28 03:41:47 +00009679 c = *q;
9680 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009681 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009682 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009683 q++;
9684 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009685 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009686 break;
9687 }
Eric Andersencb57d552001-06-28 07:25:16 +00009688 }
9689
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009690 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009691 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9692 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009693 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009694 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009695 }
9696 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009697 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009698
Eric Andersencb57d552001-06-28 07:25:16 +00009699 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009700 char save = *q;
9701 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009702 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009703 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009704 }
9705
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009706 pgetc_debug("preadbuffer at %d:%p'%s'",
9707 g_parsefile->left_in_line,
9708 g_parsefile->next_to_pgetc,
9709 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009710 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009711}
9712
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009713#define pgetc_as_macro() \
9714 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009715 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009716 : preadbuffer() \
9717 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009718
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009719static int
9720pgetc(void)
9721{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009722 pgetc_debug("pgetc_fast at %d:%p'%s'",
9723 g_parsefile->left_in_line,
9724 g_parsefile->next_to_pgetc,
9725 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009726 return pgetc_as_macro();
9727}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009728
9729#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009730# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009731#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009732# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009733#endif
9734
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009735#if ENABLE_ASH_ALIAS
9736static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009737pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009738{
9739 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009740 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009741 pgetc_debug("pgetc_fast at %d:%p'%s'",
9742 g_parsefile->left_in_line,
9743 g_parsefile->next_to_pgetc,
9744 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009745 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009746 } while (c == PEOA);
9747 return c;
9748}
9749#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009750# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009751#endif
9752
9753/*
9754 * Read a line from the script.
9755 */
9756static char *
9757pfgets(char *line, int len)
9758{
9759 char *p = line;
9760 int nleft = len;
9761 int c;
9762
9763 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009764 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009765 if (c == PEOF) {
9766 if (p == line)
9767 return NULL;
9768 break;
9769 }
9770 *p++ = c;
9771 if (c == '\n')
9772 break;
9773 }
9774 *p = '\0';
9775 return line;
9776}
9777
Eric Andersenc470f442003-07-28 09:56:35 +00009778/*
9779 * Undo the last call to pgetc. Only one character may be pushed back.
9780 * PEOF may be pushed back.
9781 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009782static void
Eric Andersenc470f442003-07-28 09:56:35 +00009783pungetc(void)
9784{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009785 g_parsefile->left_in_line++;
9786 g_parsefile->next_to_pgetc--;
9787 pgetc_debug("pushed back to %d:%p'%s'",
9788 g_parsefile->left_in_line,
9789 g_parsefile->next_to_pgetc,
9790 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009791}
9792
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009793/*
9794 * To handle the "." command, a stack of input files is used. Pushfile
9795 * adds a new entry to the stack and popfile restores the previous level.
9796 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009797static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009798pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009799{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009800 struct parsefile *pf;
9801
Denis Vlasenko597906c2008-02-20 16:38:54 +00009802 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009803 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009804 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009805 /*pf->strpush = NULL; - ckzalloc did it */
9806 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009807 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009808}
9809
9810static void
9811popfile(void)
9812{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009813 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009814
Denis Vlasenkob012b102007-02-19 22:43:01 +00009815 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009816 if (pf->pf_fd >= 0)
9817 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009818 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009819 while (pf->strpush)
9820 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009821 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009822 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009823 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009824}
9825
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009826/*
9827 * Return to top level.
9828 */
9829static void
9830popallfiles(void)
9831{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009832 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009833 popfile();
9834}
9835
9836/*
9837 * Close the file(s) that the shell is reading commands from. Called
9838 * after a fork is done.
9839 */
9840static void
9841closescript(void)
9842{
9843 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009844 if (g_parsefile->pf_fd > 0) {
9845 close(g_parsefile->pf_fd);
9846 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009847 }
9848}
9849
9850/*
9851 * Like setinputfile, but takes an open file descriptor. Call this with
9852 * interrupts off.
9853 */
9854static void
9855setinputfd(int fd, int push)
9856{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009857 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009858 if (push) {
9859 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009860 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009861 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009862 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009863 if (g_parsefile->buf == NULL)
9864 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009865 g_parsefile->left_in_buffer = 0;
9866 g_parsefile->left_in_line = 0;
9867 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009868}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009869
Eric Andersenc470f442003-07-28 09:56:35 +00009870/*
9871 * Set the input to take input from a file. If push is set, push the
9872 * old input onto the stack first.
9873 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009874static int
9875setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009876{
9877 int fd;
9878 int fd2;
9879
Denis Vlasenkob012b102007-02-19 22:43:01 +00009880 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009881 fd = open(fname, O_RDONLY);
9882 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009883 if (flags & INPUT_NOFILE_OK)
9884 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009885 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009886 }
Eric Andersenc470f442003-07-28 09:56:35 +00009887 if (fd < 10) {
9888 fd2 = copyfd(fd, 10);
9889 close(fd);
9890 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009891 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009892 fd = fd2;
9893 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009894 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009895 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009896 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009897 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009898}
9899
Eric Andersencb57d552001-06-28 07:25:16 +00009900/*
9901 * Like setinputfile, but takes input from a string.
9902 */
Eric Andersenc470f442003-07-28 09:56:35 +00009903static void
9904setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009905{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009906 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009907 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009908 g_parsefile->next_to_pgetc = string;
9909 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009910 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009911 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009912 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009913}
9914
9915
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009916/* ============ mail.c
9917 *
9918 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009919 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009920
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009921#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009922
Eric Andersencb57d552001-06-28 07:25:16 +00009923#define MAXMBOXES 10
9924
Eric Andersenc470f442003-07-28 09:56:35 +00009925/* times of mailboxes */
9926static time_t mailtime[MAXMBOXES];
9927/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009928static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009929
Eric Andersencb57d552001-06-28 07:25:16 +00009930/*
Eric Andersenc470f442003-07-28 09:56:35 +00009931 * Print appropriate message(s) if mail has arrived.
9932 * If mail_var_path_changed is set,
9933 * then the value of MAIL has mail_var_path_changed,
9934 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009935 */
Eric Andersenc470f442003-07-28 09:56:35 +00009936static void
9937chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009938{
Eric Andersencb57d552001-06-28 07:25:16 +00009939 const char *mpath;
9940 char *p;
9941 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009942 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009943 struct stackmark smark;
9944 struct stat statb;
9945
Eric Andersencb57d552001-06-28 07:25:16 +00009946 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009947 mpath = mpathset() ? mpathval() : mailval();
9948 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02009949 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00009950 if (p == NULL)
9951 break;
9952 if (*p == '\0')
9953 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009954 for (q = p; *q; q++)
9955 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009956#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009957 if (q[-1] != '/')
9958 abort();
9959#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009960 q[-1] = '\0'; /* delete trailing '/' */
9961 if (stat(p, &statb) < 0) {
9962 *mtp = 0;
9963 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009964 }
Eric Andersenc470f442003-07-28 09:56:35 +00009965 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9966 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02009967 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +00009968 pathopt ? pathopt : "you have mail"
9969 );
9970 }
9971 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009972 }
Eric Andersenc470f442003-07-28 09:56:35 +00009973 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009974 popstackmark(&smark);
9975}
Eric Andersencb57d552001-06-28 07:25:16 +00009976
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009977static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009978changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009979{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009980 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009981}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009982
Denis Vlasenko131ae172007-02-18 13:00:19 +00009983#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009984
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009985
9986/* ============ ??? */
9987
Eric Andersencb57d552001-06-28 07:25:16 +00009988/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009989 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009990 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009991static void
9992setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009993{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009994 char **newparam;
9995 char **ap;
9996 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009997
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009998 for (nparam = 0; argv[nparam]; nparam++)
9999 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010000 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10001 while (*argv) {
10002 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010003 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010004 *ap = NULL;
10005 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010006 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010007 shellparam.nparam = nparam;
10008 shellparam.p = newparam;
10009#if ENABLE_ASH_GETOPTS
10010 shellparam.optind = 1;
10011 shellparam.optoff = -1;
10012#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010013}
10014
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010015/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010016 * Process shell options. The global variable argptr contains a pointer
10017 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010018 *
10019 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10020 * For a non-interactive shell, an error condition encountered
10021 * by a special built-in ... shall cause the shell to write a diagnostic message
10022 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010023 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010024 * ...
10025 * Utility syntax error (option or operand error) Shall exit
10026 * ...
10027 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10028 * we see that bash does not do that (set "finishes" with error code 1 instead,
10029 * and shell continues), and people rely on this behavior!
10030 * Testcase:
10031 * set -o barfoo 2>/dev/null
10032 * echo $?
10033 *
10034 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010035 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010036static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010037plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010038{
10039 int i;
10040
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010041 if (name) {
10042 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010043 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010044 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010045 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010046 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010047 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010048 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010049 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010050 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010051 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010052 if (val) {
10053 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10054 } else {
10055 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10056 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010057 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010058 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010059}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010060static void
10061setoption(int flag, int val)
10062{
10063 int i;
10064
10065 for (i = 0; i < NOPTS; i++) {
10066 if (optletters(i) == flag) {
10067 optlist[i] = val;
10068 return;
10069 }
10070 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010071 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010072 /* NOTREACHED */
10073}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010074static int
Eric Andersenc470f442003-07-28 09:56:35 +000010075options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010076{
10077 char *p;
10078 int val;
10079 int c;
10080
10081 if (cmdline)
10082 minusc = NULL;
10083 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010084 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010085 if (c != '-' && c != '+')
10086 break;
10087 argptr++;
10088 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010089 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010090 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010091 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010092 if (!cmdline) {
10093 /* "-" means turn off -x and -v */
10094 if (p[0] == '\0')
10095 xflag = vflag = 0;
10096 /* "--" means reset params */
10097 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010098 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010099 }
Eric Andersenc470f442003-07-28 09:56:35 +000010100 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010101 }
Eric Andersencb57d552001-06-28 07:25:16 +000010102 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010103 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010104 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010105 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010106 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010107 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010108 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010109 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010110 /* it already printed err message */
10111 return 1; /* error */
10112 }
Eric Andersencb57d552001-06-28 07:25:16 +000010113 if (*argptr)
10114 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010115 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10116 isloginsh = 1;
10117 /* bash does not accept +-login, we also won't */
10118 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010119 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010120 isloginsh = 1;
10121 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010122 } else {
10123 setoption(c, val);
10124 }
10125 }
10126 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010127 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010128}
10129
Eric Andersencb57d552001-06-28 07:25:16 +000010130/*
Eric Andersencb57d552001-06-28 07:25:16 +000010131 * The shift builtin command.
10132 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010133static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010134shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010135{
10136 int n;
10137 char **ap1, **ap2;
10138
10139 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010140 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010141 n = number(argv[1]);
10142 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010143 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010144 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010145 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010146 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010147 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010148 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010149 }
10150 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010151 while ((*ap2++ = *ap1++) != NULL)
10152 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010153#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010154 shellparam.optind = 1;
10155 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010156#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010157 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010158 return 0;
10159}
10160
Eric Andersencb57d552001-06-28 07:25:16 +000010161/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010162 * POSIX requires that 'set' (but not export or readonly) output the
10163 * variables in lexicographic order - by the locale's collating order (sigh).
10164 * Maybe we could keep them in an ordered balanced binary tree
10165 * instead of hashed lists.
10166 * For now just roll 'em through qsort for printing...
10167 */
10168static int
10169showvars(const char *sep_prefix, int on, int off)
10170{
10171 const char *sep;
10172 char **ep, **epend;
10173
10174 ep = listvars(on, off, &epend);
10175 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10176
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010177 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010178
10179 for (; ep < epend; ep++) {
10180 const char *p;
10181 const char *q;
10182
10183 p = strchrnul(*ep, '=');
10184 q = nullstr;
10185 if (*p)
10186 q = single_quote(++p);
10187 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10188 }
10189 return 0;
10190}
10191
10192/*
Eric Andersencb57d552001-06-28 07:25:16 +000010193 * The set command builtin.
10194 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010195static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010196setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010197{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010198 int retval;
10199
Denis Vlasenko68404f12008-03-17 09:00:54 +000010200 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010201 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010202 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010203 retval = 1;
10204 if (!options(0)) { /* if no parse error... */
10205 retval = 0;
10206 optschanged();
10207 if (*argptr != NULL) {
10208 setparam(argptr);
10209 }
Eric Andersencb57d552001-06-28 07:25:16 +000010210 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010211 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010212 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010213}
10214
Denis Vlasenko131ae172007-02-18 13:00:19 +000010215#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010216static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010217change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010218{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010219 uint32_t t;
10220
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010221 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010222 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010223 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010224 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010225 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010226 vrandom.flags &= ~VNOFUNC;
10227 } else {
10228 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010229 t = strtoul(value, NULL, 10);
10230 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010231 }
Eric Andersenef02f822004-03-11 13:34:24 +000010232}
Eric Andersen16767e22004-03-16 05:14:10 +000010233#endif
10234
Denis Vlasenko131ae172007-02-18 13:00:19 +000010235#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010236static int
Eric Andersenc470f442003-07-28 09:56:35 +000010237getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010238{
10239 char *p, *q;
10240 char c = '?';
10241 int done = 0;
10242 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010243 char s[12];
10244 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010245
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010246 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010247 return 1;
10248 optnext = optfirst + *param_optind - 1;
10249
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010250 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010251 p = NULL;
10252 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010253 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010254 if (p == NULL || *p == '\0') {
10255 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010256 p = *optnext;
10257 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010258 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010259 p = NULL;
10260 done = 1;
10261 goto out;
10262 }
10263 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010264 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010265 goto atend;
10266 }
10267
10268 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010269 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010270 if (*q == '\0') {
10271 if (optstr[0] == ':') {
10272 s[0] = c;
10273 s[1] = '\0';
10274 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010275 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010276 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010277 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010278 }
10279 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010280 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010281 }
10282 if (*++q == ':')
10283 q++;
10284 }
10285
10286 if (*++q == ':') {
10287 if (*p == '\0' && (p = *optnext) == NULL) {
10288 if (optstr[0] == ':') {
10289 s[0] = c;
10290 s[1] = '\0';
10291 err |= setvarsafe("OPTARG", s, 0);
10292 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010293 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010294 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010295 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010296 c = '?';
10297 }
Eric Andersenc470f442003-07-28 09:56:35 +000010298 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010299 }
10300
10301 if (p == *optnext)
10302 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010303 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010304 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010305 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010306 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010307 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010308 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010309 *param_optind = optnext - optfirst + 1;
10310 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010311 err |= setvarsafe("OPTIND", s, VNOFUNC);
10312 s[0] = c;
10313 s[1] = '\0';
10314 err |= setvarsafe(optvar, s, 0);
10315 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010316 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010317 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010318 flush_stdout_stderr();
10319 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010320 }
10321 return done;
10322}
Eric Andersenc470f442003-07-28 09:56:35 +000010323
10324/*
10325 * The getopts builtin. Shellparam.optnext points to the next argument
10326 * to be processed. Shellparam.optptr points to the next character to
10327 * be processed in the current argument. If shellparam.optnext is NULL,
10328 * then it's the first time getopts has been called.
10329 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010330static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010331getoptscmd(int argc, char **argv)
10332{
10333 char **optbase;
10334
10335 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010336 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010337 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010338 optbase = shellparam.p;
10339 if (shellparam.optind > shellparam.nparam + 1) {
10340 shellparam.optind = 1;
10341 shellparam.optoff = -1;
10342 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010343 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010344 optbase = &argv[3];
10345 if (shellparam.optind > argc - 2) {
10346 shellparam.optind = 1;
10347 shellparam.optoff = -1;
10348 }
10349 }
10350
10351 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010352 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010353}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010354#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010355
Eric Andersencb57d552001-06-28 07:25:16 +000010356
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010357/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010358
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010359struct heredoc {
10360 struct heredoc *next; /* next here document in list */
10361 union node *here; /* redirection node */
10362 char *eofmark; /* string indicating end of input */
10363 smallint striptabs; /* if set, strip leading tabs */
10364};
10365
10366static smallint tokpushback; /* last token pushed back */
10367static smallint parsebackquote; /* nonzero if we are inside backquotes */
10368static smallint quoteflag; /* set if (part of) last token was quoted */
10369static token_id_t lasttoken; /* last token read (integer id Txxx) */
10370static struct heredoc *heredoclist; /* list of here documents to read */
10371static char *wordtext; /* text of last word returned by readtoken */
10372static struct nodelist *backquotelist;
10373static union node *redirnode;
10374static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010375
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010376static const char *
10377tokname(char *buf, int tok)
10378{
10379 if (tok < TSEMI)
10380 return tokname_array[tok] + 1;
10381 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10382 return buf;
10383}
10384
10385/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010386 * Called when an unexpected token is read during the parse. The argument
10387 * is the token that is expected, or -1 if more than one type of token can
10388 * occur at this point.
10389 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010390static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010391static void
10392raise_error_unexpected_syntax(int token)
10393{
10394 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010395 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010396 int l;
10397
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010398 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010399 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010400 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010401 raise_error_syntax(msg);
10402 /* NOTREACHED */
10403}
Eric Andersencb57d552001-06-28 07:25:16 +000010404
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010405#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010406
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010407/* parsing is heavily cross-recursive, need these forward decls */
10408static union node *andor(void);
10409static union node *pipeline(void);
10410static union node *parse_command(void);
10411static void parseheredoc(void);
10412static char peektoken(void);
10413static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010414
Eric Andersenc470f442003-07-28 09:56:35 +000010415static union node *
10416list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010417{
10418 union node *n1, *n2, *n3;
10419 int tok;
10420
Eric Andersenc470f442003-07-28 09:56:35 +000010421 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10422 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010423 return NULL;
10424 n1 = NULL;
10425 for (;;) {
10426 n2 = andor();
10427 tok = readtoken();
10428 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010429 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010430 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010431 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010432 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010433 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010434 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010435 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010436 n2 = n3;
10437 }
10438 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010439 }
10440 }
10441 if (n1 == NULL) {
10442 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010443 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010444 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010445 n3->type = NSEMI;
10446 n3->nbinary.ch1 = n1;
10447 n3->nbinary.ch2 = n2;
10448 n1 = n3;
10449 }
10450 switch (tok) {
10451 case TBACKGND:
10452 case TSEMI:
10453 tok = readtoken();
10454 /* fall through */
10455 case TNL:
10456 if (tok == TNL) {
10457 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010458 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010459 return n1;
10460 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010461 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010462 }
Eric Andersenc470f442003-07-28 09:56:35 +000010463 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010464 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010465 return n1;
10466 break;
10467 case TEOF:
10468 if (heredoclist)
10469 parseheredoc();
10470 else
Eric Andersenc470f442003-07-28 09:56:35 +000010471 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010472 return n1;
10473 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010474 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010475 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010476 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010477 return n1;
10478 }
10479 }
10480}
10481
Eric Andersenc470f442003-07-28 09:56:35 +000010482static union node *
10483andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010484{
Eric Andersencb57d552001-06-28 07:25:16 +000010485 union node *n1, *n2, *n3;
10486 int t;
10487
Eric Andersencb57d552001-06-28 07:25:16 +000010488 n1 = pipeline();
10489 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010490 t = readtoken();
10491 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010492 t = NAND;
10493 } else if (t == TOR) {
10494 t = NOR;
10495 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010496 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010497 return n1;
10498 }
Eric Andersenc470f442003-07-28 09:56:35 +000010499 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010500 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010501 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010502 n3->type = t;
10503 n3->nbinary.ch1 = n1;
10504 n3->nbinary.ch2 = n2;
10505 n1 = n3;
10506 }
10507}
10508
Eric Andersenc470f442003-07-28 09:56:35 +000010509static union node *
10510pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010511{
Eric Andersencb57d552001-06-28 07:25:16 +000010512 union node *n1, *n2, *pipenode;
10513 struct nodelist *lp, *prev;
10514 int negate;
10515
10516 negate = 0;
10517 TRACE(("pipeline: entered\n"));
10518 if (readtoken() == TNOT) {
10519 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010520 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010521 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010522 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010523 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010524 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010525 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010526 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010527 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010528 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010529 pipenode->npipe.cmdlist = lp;
10530 lp->n = n1;
10531 do {
10532 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010533 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010534 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010535 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010536 prev->next = lp;
10537 } while (readtoken() == TPIPE);
10538 lp->next = NULL;
10539 n1 = pipenode;
10540 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010541 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010542 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010543 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010544 n2->type = NNOT;
10545 n2->nnot.com = n1;
10546 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010547 }
10548 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010549}
10550
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010551static union node *
10552makename(void)
10553{
10554 union node *n;
10555
Denis Vlasenko597906c2008-02-20 16:38:54 +000010556 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010557 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010558 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010559 n->narg.text = wordtext;
10560 n->narg.backquote = backquotelist;
10561 return n;
10562}
10563
10564static void
10565fixredir(union node *n, const char *text, int err)
10566{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010567 int fd;
10568
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010569 TRACE(("Fix redir %s %d\n", text, err));
10570 if (!err)
10571 n->ndup.vname = NULL;
10572
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010573 fd = bb_strtou(text, NULL, 10);
10574 if (!errno && fd >= 0)
10575 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010576 else if (LONE_DASH(text))
10577 n->ndup.dupfd = -1;
10578 else {
10579 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010580 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010581 n->ndup.vname = makename();
10582 }
10583}
10584
10585/*
10586 * Returns true if the text contains nothing to expand (no dollar signs
10587 * or backquotes).
10588 */
10589static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010590noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010591{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010592 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010593
Denys Vlasenkocd716832009-11-28 22:14:02 +010010594 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010595 if (c == CTLQUOTEMARK)
10596 continue;
10597 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010598 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010599 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010600 return 0;
10601 }
10602 return 1;
10603}
10604
10605static void
10606parsefname(void)
10607{
10608 union node *n = redirnode;
10609
10610 if (readtoken() != TWORD)
10611 raise_error_unexpected_syntax(-1);
10612 if (n->type == NHERE) {
10613 struct heredoc *here = heredoc;
10614 struct heredoc *p;
10615 int i;
10616
10617 if (quoteflag == 0)
10618 n->type = NXHERE;
10619 TRACE(("Here document %d\n", n->type));
10620 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010621 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010622 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010623 here->eofmark = wordtext;
10624 here->next = NULL;
10625 if (heredoclist == NULL)
10626 heredoclist = here;
10627 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010628 for (p = heredoclist; p->next; p = p->next)
10629 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010630 p->next = here;
10631 }
10632 } else if (n->type == NTOFD || n->type == NFROMFD) {
10633 fixredir(n, wordtext, 0);
10634 } else {
10635 n->nfile.fname = makename();
10636 }
10637}
Eric Andersencb57d552001-06-28 07:25:16 +000010638
Eric Andersenc470f442003-07-28 09:56:35 +000010639static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010640simplecmd(void)
10641{
10642 union node *args, **app;
10643 union node *n = NULL;
10644 union node *vars, **vpp;
10645 union node **rpp, *redir;
10646 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010647#if ENABLE_ASH_BASH_COMPAT
10648 smallint double_brackets_flag = 0;
10649#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010650
10651 args = NULL;
10652 app = &args;
10653 vars = NULL;
10654 vpp = &vars;
10655 redir = NULL;
10656 rpp = &redir;
10657
10658 savecheckkwd = CHKALIAS;
10659 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010660 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010661 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010662 t = readtoken();
10663 switch (t) {
10664#if ENABLE_ASH_BASH_COMPAT
10665 case TAND: /* "&&" */
10666 case TOR: /* "||" */
10667 if (!double_brackets_flag) {
10668 tokpushback = 1;
10669 goto out;
10670 }
10671 wordtext = (char *) (t == TAND ? "-a" : "-o");
10672#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010673 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010674 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010675 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010676 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010677 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010678#if ENABLE_ASH_BASH_COMPAT
10679 if (strcmp("[[", wordtext) == 0)
10680 double_brackets_flag = 1;
10681 else if (strcmp("]]", wordtext) == 0)
10682 double_brackets_flag = 0;
10683#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010684 n->narg.backquote = backquotelist;
10685 if (savecheckkwd && isassignment(wordtext)) {
10686 *vpp = n;
10687 vpp = &n->narg.next;
10688 } else {
10689 *app = n;
10690 app = &n->narg.next;
10691 savecheckkwd = 0;
10692 }
10693 break;
10694 case TREDIR:
10695 *rpp = n = redirnode;
10696 rpp = &n->nfile.next;
10697 parsefname(); /* read name of redirection file */
10698 break;
10699 case TLP:
10700 if (args && app == &args->narg.next
10701 && !vars && !redir
10702 ) {
10703 struct builtincmd *bcmd;
10704 const char *name;
10705
10706 /* We have a function */
10707 if (readtoken() != TRP)
10708 raise_error_unexpected_syntax(TRP);
10709 name = n->narg.text;
10710 if (!goodname(name)
10711 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10712 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010713 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010714 }
10715 n->type = NDEFUN;
10716 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10717 n->narg.next = parse_command();
10718 return n;
10719 }
10720 /* fall through */
10721 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010722 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010723 goto out;
10724 }
10725 }
10726 out:
10727 *app = NULL;
10728 *vpp = NULL;
10729 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010730 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010731 n->type = NCMD;
10732 n->ncmd.args = args;
10733 n->ncmd.assign = vars;
10734 n->ncmd.redirect = redir;
10735 return n;
10736}
10737
10738static union node *
10739parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010740{
Eric Andersencb57d552001-06-28 07:25:16 +000010741 union node *n1, *n2;
10742 union node *ap, **app;
10743 union node *cp, **cpp;
10744 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010745 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010746 int t;
10747
10748 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010749 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010750
Eric Andersencb57d552001-06-28 07:25:16 +000010751 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010752 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010753 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010754 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010755 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010756 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010757 n1->type = NIF;
10758 n1->nif.test = list(0);
10759 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010760 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010761 n1->nif.ifpart = list(0);
10762 n2 = n1;
10763 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010764 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010765 n2 = n2->nif.elsepart;
10766 n2->type = NIF;
10767 n2->nif.test = list(0);
10768 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010769 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010770 n2->nif.ifpart = list(0);
10771 }
10772 if (lasttoken == TELSE)
10773 n2->nif.elsepart = list(0);
10774 else {
10775 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010776 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010777 }
Eric Andersenc470f442003-07-28 09:56:35 +000010778 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010779 break;
10780 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010781 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010782 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010783 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010784 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010785 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010786 got = readtoken();
10787 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010788 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010789 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010790 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010791 }
10792 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010793 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010794 break;
10795 }
10796 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010797 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010798 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010799 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010800 n1->type = NFOR;
10801 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010802 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010803 if (readtoken() == TIN) {
10804 app = &ap;
10805 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010806 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010807 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010808 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010809 n2->narg.text = wordtext;
10810 n2->narg.backquote = backquotelist;
10811 *app = n2;
10812 app = &n2->narg.next;
10813 }
10814 *app = NULL;
10815 n1->nfor.args = ap;
10816 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010817 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010818 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010819 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010820 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010821 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010822 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010823 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010824 n1->nfor.args = n2;
10825 /*
10826 * Newline or semicolon here is optional (but note
10827 * that the original Bourne shell only allowed NL).
10828 */
10829 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010830 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010831 }
Eric Andersenc470f442003-07-28 09:56:35 +000010832 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010833 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010834 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010835 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010836 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010837 break;
10838 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010839 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010840 n1->type = NCASE;
10841 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010842 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010843 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010844 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010845 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010846 n2->narg.text = wordtext;
10847 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010848 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010849 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010850 } while (readtoken() == TNL);
10851 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010852 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010853 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010854 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010855 checkkwd = CHKNL | CHKKWD;
10856 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010857 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010858 if (lasttoken == TLP)
10859 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010860 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010861 cp->type = NCLIST;
10862 app = &cp->nclist.pattern;
10863 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010864 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010865 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010866 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010867 ap->narg.text = wordtext;
10868 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010869 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010870 break;
10871 app = &ap->narg.next;
10872 readtoken();
10873 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010874 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010875 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010876 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010877 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010878
Eric Andersenc470f442003-07-28 09:56:35 +000010879 cpp = &cp->nclist.next;
10880
10881 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010882 t = readtoken();
10883 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010884 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010885 raise_error_unexpected_syntax(TENDCASE);
10886 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010887 }
Eric Andersenc470f442003-07-28 09:56:35 +000010888 }
Eric Andersencb57d552001-06-28 07:25:16 +000010889 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010890 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010891 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010892 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010893 n1->type = NSUBSHELL;
10894 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010895 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010896 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010897 break;
10898 case TBEGIN:
10899 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010900 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010901 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010902 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010903 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010904 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010905 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010906 }
10907
Eric Andersenc470f442003-07-28 09:56:35 +000010908 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010909 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010910
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010911 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010912 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010913 checkkwd = CHKKWD | CHKALIAS;
10914 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010915 while (readtoken() == TREDIR) {
10916 *rpp = n2 = redirnode;
10917 rpp = &n2->nfile.next;
10918 parsefname();
10919 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010920 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010921 *rpp = NULL;
10922 if (redir) {
10923 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010924 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010925 n2->type = NREDIR;
10926 n2->nredir.n = n1;
10927 n1 = n2;
10928 }
10929 n1->nredir.redirect = redir;
10930 }
Eric Andersencb57d552001-06-28 07:25:16 +000010931 return n1;
10932}
10933
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010934#if ENABLE_ASH_BASH_COMPAT
10935static int decode_dollar_squote(void)
10936{
10937 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10938 int c, cnt;
10939 char *p;
10940 char buf[4];
10941
10942 c = pgetc();
10943 p = strchr(C_escapes, c);
10944 if (p) {
10945 buf[0] = c;
10946 p = buf;
10947 cnt = 3;
10948 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10949 do {
10950 c = pgetc();
10951 *++p = c;
10952 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10953 pungetc();
10954 } else if (c == 'x') { /* \xHH */
10955 do {
10956 c = pgetc();
10957 *++p = c;
10958 } while (isxdigit(c) && --cnt);
10959 pungetc();
10960 if (cnt == 3) { /* \x but next char is "bad" */
10961 c = 'x';
10962 goto unrecognized;
10963 }
10964 } else { /* simple seq like \\ or \t */
10965 p++;
10966 }
10967 *p = '\0';
10968 p = buf;
10969 c = bb_process_escape_sequence((void*)&p);
10970 } else { /* unrecognized "\z": print both chars unless ' or " */
10971 if (c != '\'' && c != '"') {
10972 unrecognized:
10973 c |= 0x100; /* "please encode \, then me" */
10974 }
10975 }
10976 return c;
10977}
10978#endif
10979
Eric Andersencb57d552001-06-28 07:25:16 +000010980/*
10981 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10982 * is not NULL, read a here document. In the latter case, eofmark is the
10983 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010010984 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000010985 * is the first character of the input token or document.
10986 *
10987 * Because C does not have internal subroutines, I have simulated them
10988 * using goto's to implement the subroutine linkage. The following macros
10989 * will run code that appears at the end of readtoken1.
10990 */
Eric Andersen2870d962001-07-02 17:27:21 +000010991#define CHECKEND() {goto checkend; checkend_return:;}
10992#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10993#define PARSESUB() {goto parsesub; parsesub_return:;}
10994#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10995#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10996#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010997static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010010998readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010999{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011000 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011001 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011002 char *out;
11003 int len;
11004 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011005 struct nodelist *bqlist;
11006 smallint quotef;
11007 smallint dblquote;
11008 smallint oldstyle;
11009 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011010#if ENABLE_ASH_EXPAND_PRMT
11011 smallint pssyntax; /* we are expanding a prompt string */
11012#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011013 int varnest; /* levels of variables expansion */
11014 int arinest; /* levels of arithmetic expansion */
11015 int parenlevel; /* levels of parens in arithmetic */
11016 int dqvarnest; /* levels of variables expansion within double quotes */
11017
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011018 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011019
Eric Andersencb57d552001-06-28 07:25:16 +000011020#if __GNUC__
11021 /* Avoid longjmp clobbering */
11022 (void) &out;
11023 (void) &quotef;
11024 (void) &dblquote;
11025 (void) &varnest;
11026 (void) &arinest;
11027 (void) &parenlevel;
11028 (void) &dqvarnest;
11029 (void) &oldstyle;
11030 (void) &prevsyntax;
11031 (void) &syntax;
11032#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011033 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011034 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011035 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011036 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 */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011051 CHECKEND(); /* set c to PEOF if at end of here document */
11052 for (;;) { /* until end of line or end of word */
11053 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11054 switch (SIT(c, syntax)) {
11055 case CNL: /* '\n' */
11056 if (syntax == BASESYNTAX)
11057 goto endword; /* exit outer loop */
11058 USTPUTC(c, out);
11059 g_parsefile->linno++;
11060 setprompt_if(doprompt, 2);
11061 c = pgetc();
11062 goto loop; /* continue outer loop */
11063 case CWORD:
11064 USTPUTC(c, out);
11065 break;
11066 case CCTL:
11067 if (eofmark == NULL || dblquote)
11068 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011069#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011070 if (c == '\\' && bash_dollar_squote) {
11071 c = decode_dollar_squote();
11072 if (c & 0x100) {
11073 USTPUTC('\\', out);
11074 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011075 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011076 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011077#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011078 USTPUTC(c, out);
11079 break;
11080 case CBACK: /* backslash */
11081 c = pgetc_without_PEOA();
11082 if (c == PEOF) {
11083 USTPUTC(CTLESC, out);
11084 USTPUTC('\\', out);
11085 pungetc();
11086 } else if (c == '\n') {
11087 setprompt_if(doprompt, 2);
11088 } else {
11089#if ENABLE_ASH_EXPAND_PRMT
11090 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011091 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011092 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011093 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011094#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011095 /* Backslash is retained if we are in "str" and next char isn't special */
11096 if (dblquote
11097 && c != '\\'
11098 && c != '`'
11099 && c != '$'
11100 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011101 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011102 USTPUTC(CTLESC, out);
11103 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011104 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011105 if (SIT(c, SQSYNTAX) == CCTL)
11106 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011107 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011108 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011109 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011110 break;
11111 case CSQUOTE:
11112 syntax = SQSYNTAX;
11113 quotemark:
11114 if (eofmark == NULL) {
11115 USTPUTC(CTLQUOTEMARK, out);
11116 }
11117 break;
11118 case CDQUOTE:
11119 syntax = DQSYNTAX;
11120 dblquote = 1;
11121 goto quotemark;
11122 case CENDQUOTE:
11123 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11124 if (eofmark != NULL && arinest == 0
11125 && varnest == 0
11126 ) {
11127 USTPUTC(c, out);
11128 } else {
11129 if (dqvarnest == 0) {
11130 syntax = BASESYNTAX;
11131 dblquote = 0;
11132 }
11133 quotef = 1;
11134 goto quotemark;
11135 }
11136 break;
11137 case CVAR: /* '$' */
11138 PARSESUB(); /* parse substitution */
11139 break;
11140 case CENDVAR: /* '}' */
11141 if (varnest > 0) {
11142 varnest--;
11143 if (dqvarnest > 0) {
11144 dqvarnest--;
11145 }
11146 c = CTLENDVAR;
11147 }
11148 USTPUTC(c, out);
11149 break;
11150#if ENABLE_SH_MATH_SUPPORT
11151 case CLP: /* '(' in arithmetic */
11152 parenlevel++;
11153 USTPUTC(c, out);
11154 break;
11155 case CRP: /* ')' in arithmetic */
11156 if (parenlevel > 0) {
11157 parenlevel--;
11158 } else {
11159 if (pgetc() == ')') {
11160 if (--arinest == 0) {
11161 syntax = prevsyntax;
11162 dblquote = (syntax == DQSYNTAX);
11163 c = CTLENDARI;
11164 }
11165 } else {
11166 /*
11167 * unbalanced parens
11168 * (don't 2nd guess - no error)
11169 */
11170 pungetc();
11171 }
11172 }
11173 USTPUTC(c, out);
11174 break;
11175#endif
11176 case CBQUOTE: /* '`' */
11177 PARSEBACKQOLD();
11178 break;
11179 case CENDFILE:
11180 goto endword; /* exit outer loop */
11181 case CIGN:
11182 break;
11183 default:
11184 if (varnest == 0) {
11185#if ENABLE_ASH_BASH_COMPAT
11186 if (c == '&') {
11187 if (pgetc() == '>')
11188 c = 0x100 + '>'; /* flag &> */
11189 pungetc();
11190 }
11191#endif
11192 goto endword; /* exit outer loop */
11193 }
11194 IF_ASH_ALIAS(if (c != PEOA))
11195 USTPUTC(c, out);
11196 }
11197 c = pgetc_fast();
11198 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011199 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011200
Mike Frysinger98c52642009-04-02 10:02:37 +000011201#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011202 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011203 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011204#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011205 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011206 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011207 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011208 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011209 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011210 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011211 }
11212 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011213 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011214 out = stackblock();
11215 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011216 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011217 && quotef == 0
11218 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011219 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011220 PARSEREDIR(); /* passed as params: out, c */
11221 lasttoken = TREDIR;
11222 return lasttoken;
11223 }
11224 /* else: non-number X seen, interpret it
11225 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011226 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011227 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011228 }
11229 quoteflag = quotef;
11230 backquotelist = bqlist;
11231 grabstackblock(len);
11232 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011233 lasttoken = TWORD;
11234 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011235/* end of readtoken routine */
11236
Eric Andersencb57d552001-06-28 07:25:16 +000011237/*
11238 * Check to see whether we are at the end of the here document. When this
11239 * is called, c is set to the first character of the next input line. If
11240 * we are at the end of the here document, this routine sets the c to PEOF.
11241 */
Eric Andersenc470f442003-07-28 09:56:35 +000011242checkend: {
11243 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011244#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011245 if (c == PEOA)
11246 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011247#endif
11248 if (striptabs) {
11249 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011250 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011251 }
Eric Andersenc470f442003-07-28 09:56:35 +000011252 }
11253 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011254 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011255 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011256
Eric Andersenc470f442003-07-28 09:56:35 +000011257 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011258 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11259 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011260 if (*p == '\n' && *q == '\0') {
11261 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011262 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011263 needprompt = doprompt;
11264 } else {
11265 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011266 }
11267 }
11268 }
11269 }
Eric Andersenc470f442003-07-28 09:56:35 +000011270 goto checkend_return;
11271}
Eric Andersencb57d552001-06-28 07:25:16 +000011272
Eric Andersencb57d552001-06-28 07:25:16 +000011273/*
11274 * Parse a redirection operator. The variable "out" points to a string
11275 * specifying the fd to be redirected. The variable "c" contains the
11276 * first character of the redirection operator.
11277 */
Eric Andersenc470f442003-07-28 09:56:35 +000011278parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011279 /* out is already checked to be a valid number or "" */
11280 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011281 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011282
Denis Vlasenko597906c2008-02-20 16:38:54 +000011283 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011284 if (c == '>') {
11285 np->nfile.fd = 1;
11286 c = pgetc();
11287 if (c == '>')
11288 np->type = NAPPEND;
11289 else if (c == '|')
11290 np->type = NCLOBBER;
11291 else if (c == '&')
11292 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011293 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011294 else {
11295 np->type = NTO;
11296 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011297 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011298 }
11299#if ENABLE_ASH_BASH_COMPAT
11300 else if (c == 0x100 + '>') { /* this flags &> redirection */
11301 np->nfile.fd = 1;
11302 pgetc(); /* this is '>', no need to check */
11303 np->type = NTO2;
11304 }
11305#endif
11306 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011307 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011308 c = pgetc();
11309 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011310 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011311 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011312 np = stzalloc(sizeof(struct nhere));
11313 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011314 }
11315 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011316 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011317 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011318 c = pgetc();
11319 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011320 heredoc->striptabs = 1;
11321 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011322 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011323 pungetc();
11324 }
11325 break;
11326
11327 case '&':
11328 np->type = NFROMFD;
11329 break;
11330
11331 case '>':
11332 np->type = NFROMTO;
11333 break;
11334
11335 default:
11336 np->type = NFROM;
11337 pungetc();
11338 break;
11339 }
Eric Andersencb57d552001-06-28 07:25:16 +000011340 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011341 if (fd >= 0)
11342 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011343 redirnode = np;
11344 goto parseredir_return;
11345}
Eric Andersencb57d552001-06-28 07:25:16 +000011346
Eric Andersencb57d552001-06-28 07:25:16 +000011347/*
11348 * Parse a substitution. At this point, we have read the dollar sign
11349 * and nothing else.
11350 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011351
11352/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11353 * (assuming ascii char codes, as the original implementation did) */
11354#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011355 (((unsigned)(c) - 33 < 32) \
11356 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011357parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011358 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011359 int typeloc;
11360 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011361
Eric Andersenc470f442003-07-28 09:56:35 +000011362 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011363 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011364 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011365 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011366#if ENABLE_ASH_BASH_COMPAT
11367 if (c == '\'')
11368 bash_dollar_squote = 1;
11369 else
11370#endif
11371 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011372 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011373 } else if (c == '(') {
11374 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011375 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011376#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011377 PARSEARITH();
11378#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011379 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011380#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011381 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011382 pungetc();
11383 PARSEBACKQNEW();
11384 }
11385 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011386 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011387 USTPUTC(CTLVAR, out);
11388 typeloc = out - (char *)stackblock();
11389 USTPUTC(VSNORMAL, out);
11390 subtype = VSNORMAL;
11391 if (c == '{') {
11392 c = pgetc();
11393 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011394 c = pgetc();
11395 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011396 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011397 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011398 subtype = VSLENGTH; /* ${#VAR} */
11399 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011400 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011401 }
Eric Andersenc470f442003-07-28 09:56:35 +000011402 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011403 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011404 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011405 do {
11406 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011407 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011408 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011409 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011410 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011411 do {
11412 STPUTC(c, out);
11413 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011414 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011415 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011416 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011417 USTPUTC(c, out);
11418 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011419 } else {
11420 badsub:
11421 raise_error_syntax("bad substitution");
11422 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011423 if (c != '}' && subtype == VSLENGTH) {
11424 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011425 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011426 }
Eric Andersencb57d552001-06-28 07:25:16 +000011427
Eric Andersenc470f442003-07-28 09:56:35 +000011428 STPUTC('=', out);
11429 flags = 0;
11430 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011431 /* ${VAR...} but not $VAR or ${#VAR} */
11432 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011433 switch (c) {
11434 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011435 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011436#if ENABLE_ASH_BASH_COMPAT
11437 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011438//TODO: support more general format ${v:EXPR:EXPR},
11439// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011440 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011441 pungetc();
11442 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011443 }
11444#endif
11445 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011446 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011447 default: {
11448 static const char types[] ALIGN1 = "}-+?=";
11449 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011450 if (p == NULL)
11451 goto badsub;
11452 subtype = p - types + VSNORMAL;
11453 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011454 }
Eric Andersenc470f442003-07-28 09:56:35 +000011455 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011456 case '#': {
11457 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011458 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011459 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011460 if (c != cc)
11461 goto do_pungetc;
11462 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011463 break;
11464 }
11465#if ENABLE_ASH_BASH_COMPAT
11466 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011467 /* ${v/[/]pattern/repl} */
11468//TODO: encode pattern and repl separately.
11469// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011470 subtype = VSREPLACE;
11471 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011472 if (c != '/')
11473 goto do_pungetc;
11474 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011475 break;
11476#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011477 }
Eric Andersenc470f442003-07-28 09:56:35 +000011478 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011479 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011480 pungetc();
11481 }
11482 if (dblquote || arinest)
11483 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011484 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011485 if (subtype != VSNORMAL) {
11486 varnest++;
11487 if (dblquote || arinest) {
11488 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011489 }
11490 }
11491 }
Eric Andersenc470f442003-07-28 09:56:35 +000011492 goto parsesub_return;
11493}
Eric Andersencb57d552001-06-28 07:25:16 +000011494
Eric Andersencb57d552001-06-28 07:25:16 +000011495/*
11496 * Called to parse command substitutions. Newstyle is set if the command
11497 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11498 * list of commands (passed by reference), and savelen is the number of
11499 * characters on the top of the stack which must be preserved.
11500 */
Eric Andersenc470f442003-07-28 09:56:35 +000011501parsebackq: {
11502 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011503 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011504 union node *n;
11505 char *volatile str;
11506 struct jmploc jmploc;
11507 struct jmploc *volatile savehandler;
11508 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011509 smallint saveprompt = 0;
11510
Eric Andersencb57d552001-06-28 07:25:16 +000011511#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011512 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011513#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011514 savepbq = parsebackquote;
11515 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011516 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011517 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011518 exception_handler = savehandler;
11519 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011520 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011521 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011522 str = NULL;
11523 savelen = out - (char *)stackblock();
11524 if (savelen > 0) {
11525 str = ckmalloc(savelen);
11526 memcpy(str, stackblock(), savelen);
11527 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011528 savehandler = exception_handler;
11529 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011530 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011531 if (oldstyle) {
11532 /* We must read until the closing backquote, giving special
11533 treatment to some slashes, and then push the string and
11534 reread it as input, interpreting it normally. */
11535 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011536 size_t psavelen;
11537 char *pstr;
11538
Eric Andersenc470f442003-07-28 09:56:35 +000011539 STARTSTACKSTR(pout);
11540 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011541 int pc;
11542
11543 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011544 pc = pgetc();
11545 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011546 case '`':
11547 goto done;
11548
11549 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011550 pc = pgetc();
11551 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011552 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011553 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011554 /*
11555 * If eating a newline, avoid putting
11556 * the newline into the new character
11557 * stream (via the STPUTC after the
11558 * switch).
11559 */
11560 continue;
11561 }
11562 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011563 && (!dblquote || pc != '"')
11564 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011565 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011566 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011567 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011568 break;
11569 }
11570 /* fall through */
11571
11572 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011573 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011574 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011575 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011576
11577 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011578 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011579 needprompt = doprompt;
11580 break;
11581
11582 default:
11583 break;
11584 }
11585 STPUTC(pc, pout);
11586 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011587 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011588 STPUTC('\0', pout);
11589 psavelen = pout - (char *)stackblock();
11590 if (psavelen > 0) {
11591 pstr = grabstackstr(pout);
11592 setinputstring(pstr);
11593 }
11594 }
11595 nlpp = &bqlist;
11596 while (*nlpp)
11597 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011598 *nlpp = stzalloc(sizeof(**nlpp));
11599 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011600 parsebackquote = oldstyle;
11601
11602 if (oldstyle) {
11603 saveprompt = doprompt;
11604 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011605 }
11606
Eric Andersenc470f442003-07-28 09:56:35 +000011607 n = list(2);
11608
11609 if (oldstyle)
11610 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011611 else if (readtoken() != TRP)
11612 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011613
11614 (*nlpp)->n = n;
11615 if (oldstyle) {
11616 /*
11617 * Start reading from old file again, ignoring any pushed back
11618 * tokens left from the backquote parsing
11619 */
11620 popfile();
11621 tokpushback = 0;
11622 }
11623 while (stackblocksize() <= savelen)
11624 growstackblock();
11625 STARTSTACKSTR(out);
11626 if (str) {
11627 memcpy(out, str, savelen);
11628 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011629 INT_OFF;
11630 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011631 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011632 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011633 }
11634 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011635 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011636 if (arinest || dblquote)
11637 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11638 else
11639 USTPUTC(CTLBACKQ, out);
11640 if (oldstyle)
11641 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011642 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011643}
11644
Mike Frysinger98c52642009-04-02 10:02:37 +000011645#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011646/*
11647 * Parse an arithmetic expansion (indicate start of one and set state)
11648 */
Eric Andersenc470f442003-07-28 09:56:35 +000011649parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011650 if (++arinest == 1) {
11651 prevsyntax = syntax;
11652 syntax = ARISYNTAX;
11653 USTPUTC(CTLARI, out);
11654 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011655 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011656 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011657 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011658 } else {
11659 /*
11660 * we collapse embedded arithmetic expansion to
11661 * parenthesis, which should be equivalent
11662 */
11663 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011664 }
Eric Andersenc470f442003-07-28 09:56:35 +000011665 goto parsearith_return;
11666}
11667#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011668
Eric Andersenc470f442003-07-28 09:56:35 +000011669} /* end of readtoken */
11670
Eric Andersencb57d552001-06-28 07:25:16 +000011671/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011672 * Read the next input token.
11673 * If the token is a word, we set backquotelist to the list of cmds in
11674 * backquotes. We set quoteflag to true if any part of the word was
11675 * quoted.
11676 * If the token is TREDIR, then we set redirnode to a structure containing
11677 * the redirection.
11678 * In all cases, the variable startlinno is set to the number of the line
11679 * on which the token starts.
11680 *
11681 * [Change comment: here documents and internal procedures]
11682 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11683 * word parsing code into a separate routine. In this case, readtoken
11684 * doesn't need to have any internal procedures, but parseword does.
11685 * We could also make parseoperator in essence the main routine, and
11686 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011687 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011688#define NEW_xxreadtoken
11689#ifdef NEW_xxreadtoken
11690/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011691static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011692 '\n', '(', ')', /* singles */
11693 '&', '|', ';', /* doubles */
11694 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011695};
Eric Andersencb57d552001-06-28 07:25:16 +000011696
Denis Vlasenko834dee72008-10-07 09:18:30 +000011697#define xxreadtoken_singles 3
11698#define xxreadtoken_doubles 3
11699
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011700static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011701 TNL, TLP, TRP, /* only single occurrence allowed */
11702 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11703 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011704 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011705};
11706
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011707static int
11708xxreadtoken(void)
11709{
11710 int c;
11711
11712 if (tokpushback) {
11713 tokpushback = 0;
11714 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011715 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011716 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011717 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011718 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011719 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011720 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011721 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011722
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011723 if (c == '#') {
11724 while ((c = pgetc()) != '\n' && c != PEOF)
11725 continue;
11726 pungetc();
11727 } else if (c == '\\') {
11728 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011729 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011730 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011731 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011732 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011733 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011734 } else {
11735 const char *p;
11736
11737 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11738 if (c != PEOF) {
11739 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011740 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011741 needprompt = doprompt;
11742 }
11743
11744 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011745 if (p == NULL)
11746 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011747
Denis Vlasenko834dee72008-10-07 09:18:30 +000011748 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11749 int cc = pgetc();
11750 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011751 p += xxreadtoken_doubles + 1;
11752 } else {
11753 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011754#if ENABLE_ASH_BASH_COMPAT
11755 if (c == '&' && cc == '>') /* &> */
11756 break; /* return readtoken1(...) */
11757#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011758 }
11759 }
11760 }
11761 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11762 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011763 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011764 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011765
11766 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011767}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011768#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011769#define RETURN(token) return lasttoken = token
11770static int
11771xxreadtoken(void)
11772{
11773 int c;
11774
11775 if (tokpushback) {
11776 tokpushback = 0;
11777 return lasttoken;
11778 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011779 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011780 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011781 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011782 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011783 switch (c) {
11784 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011785 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011786 continue;
11787 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011788 while ((c = pgetc()) != '\n' && c != PEOF)
11789 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011790 pungetc();
11791 continue;
11792 case '\\':
11793 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011794 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011795 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011796 continue;
11797 }
11798 pungetc();
11799 goto breakloop;
11800 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011801 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011802 needprompt = doprompt;
11803 RETURN(TNL);
11804 case PEOF:
11805 RETURN(TEOF);
11806 case '&':
11807 if (pgetc() == '&')
11808 RETURN(TAND);
11809 pungetc();
11810 RETURN(TBACKGND);
11811 case '|':
11812 if (pgetc() == '|')
11813 RETURN(TOR);
11814 pungetc();
11815 RETURN(TPIPE);
11816 case ';':
11817 if (pgetc() == ';')
11818 RETURN(TENDCASE);
11819 pungetc();
11820 RETURN(TSEMI);
11821 case '(':
11822 RETURN(TLP);
11823 case ')':
11824 RETURN(TRP);
11825 default:
11826 goto breakloop;
11827 }
11828 }
11829 breakloop:
11830 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11831#undef RETURN
11832}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011833#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011834
11835static int
11836readtoken(void)
11837{
11838 int t;
11839#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011840 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011841#endif
11842
11843#if ENABLE_ASH_ALIAS
11844 top:
11845#endif
11846
11847 t = xxreadtoken();
11848
11849 /*
11850 * eat newlines
11851 */
11852 if (checkkwd & CHKNL) {
11853 while (t == TNL) {
11854 parseheredoc();
11855 t = xxreadtoken();
11856 }
11857 }
11858
11859 if (t != TWORD || quoteflag) {
11860 goto out;
11861 }
11862
11863 /*
11864 * check for keywords
11865 */
11866 if (checkkwd & CHKKWD) {
11867 const char *const *pp;
11868
11869 pp = findkwd(wordtext);
11870 if (pp) {
11871 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011872 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011873 goto out;
11874 }
11875 }
11876
11877 if (checkkwd & CHKALIAS) {
11878#if ENABLE_ASH_ALIAS
11879 struct alias *ap;
11880 ap = lookupalias(wordtext, 1);
11881 if (ap != NULL) {
11882 if (*ap->val) {
11883 pushstring(ap->val, ap);
11884 }
11885 goto top;
11886 }
11887#endif
11888 }
11889 out:
11890 checkkwd = 0;
11891#if DEBUG
11892 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011893 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011894 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011895 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011896#endif
11897 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011898}
11899
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011900static char
11901peektoken(void)
11902{
11903 int t;
11904
11905 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011906 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011907 return tokname_array[t][0];
11908}
Eric Andersencb57d552001-06-28 07:25:16 +000011909
11910/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011911 * Read and parse a command. Returns NODE_EOF on end of file.
11912 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011913 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011914static union node *
11915parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011916{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011917 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011918
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011919 tokpushback = 0;
11920 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011921 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011922 needprompt = 0;
11923 t = readtoken();
11924 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011925 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011926 if (t == TNL)
11927 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011928 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011929 return list(1);
11930}
11931
11932/*
11933 * Input any here documents.
11934 */
11935static void
11936parseheredoc(void)
11937{
11938 struct heredoc *here;
11939 union node *n;
11940
11941 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011942 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011943
11944 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011945 setprompt_if(needprompt, 2);
11946 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011947 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011948 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011949 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011950 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011951 n->narg.text = wordtext;
11952 n->narg.backquote = backquotelist;
11953 here->here->nhere.doc = n;
11954 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011955 }
Eric Andersencb57d552001-06-28 07:25:16 +000011956}
11957
11958
11959/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011960 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011961 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011962#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011963static const char *
11964expandstr(const char *ps)
11965{
11966 union node n;
11967
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011968 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11969 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011970 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011971 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011972 popfile();
11973
11974 n.narg.type = NARG;
11975 n.narg.next = NULL;
11976 n.narg.text = wordtext;
11977 n.narg.backquote = backquotelist;
11978
11979 expandarg(&n, NULL, 0);
11980 return stackblock();
11981}
11982#endif
11983
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011984/*
11985 * Execute a command or commands contained in a string.
11986 */
11987static int
11988evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011989{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011990 union node *n;
11991 struct stackmark smark;
11992 int skip;
11993
11994 setinputstring(s);
11995 setstackmark(&smark);
11996
11997 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011998 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011999 evaltree(n, 0);
12000 popstackmark(&smark);
12001 skip = evalskip;
12002 if (skip)
12003 break;
12004 }
12005 popfile();
12006
12007 skip &= mask;
12008 evalskip = skip;
12009 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012010}
12011
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012012/*
12013 * The eval command.
12014 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012015static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012016evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012017{
12018 char *p;
12019 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012020
Denis Vlasenko68404f12008-03-17 09:00:54 +000012021 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012022 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012023 argv += 2;
12024 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012025 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012026 for (;;) {
12027 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012028 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012029 if (p == NULL)
12030 break;
12031 STPUTC(' ', concat);
12032 }
12033 STPUTC('\0', concat);
12034 p = grabstackstr(concat);
12035 }
12036 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012037 }
12038 return exitstatus;
12039}
12040
12041/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012042 * Read and execute commands.
12043 * "Top" is nonzero for the top level command loop;
12044 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012045 */
12046static int
12047cmdloop(int top)
12048{
12049 union node *n;
12050 struct stackmark smark;
12051 int inter;
12052 int numeof = 0;
12053
12054 TRACE(("cmdloop(%d) called\n", top));
12055 for (;;) {
12056 int skip;
12057
12058 setstackmark(&smark);
12059#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012060 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012061 showjobs(stderr, SHOW_CHANGED);
12062#endif
12063 inter = 0;
12064 if (iflag && top) {
12065 inter++;
12066#if ENABLE_ASH_MAIL
12067 chkmail();
12068#endif
12069 }
12070 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012071#if DEBUG
12072 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012073 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012074#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012075 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012076 if (!top || numeof >= 50)
12077 break;
12078 if (!stoppedjobs()) {
12079 if (!Iflag)
12080 break;
12081 out2str("\nUse \"exit\" to leave shell.\n");
12082 }
12083 numeof++;
12084 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012085 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12086 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012087 numeof = 0;
12088 evaltree(n, 0);
12089 }
12090 popstackmark(&smark);
12091 skip = evalskip;
12092
12093 if (skip) {
12094 evalskip = 0;
12095 return skip & SKIPEVAL;
12096 }
12097 }
12098 return 0;
12099}
12100
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012101/*
12102 * Take commands from a file. To be compatible we should do a path
12103 * search for the file, which is necessary to find sub-commands.
12104 */
12105static char *
12106find_dot_file(char *name)
12107{
12108 char *fullname;
12109 const char *path = pathval();
12110 struct stat statb;
12111
12112 /* don't try this for absolute or relative paths */
12113 if (strchr(name, '/'))
12114 return name;
12115
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012116 /* IIRC standards do not say whether . is to be searched.
12117 * And it is even smaller this way, making it unconditional for now:
12118 */
12119 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12120 fullname = name;
12121 goto try_cur_dir;
12122 }
12123
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012124 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012125 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012126 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12127 /*
12128 * Don't bother freeing here, since it will
12129 * be freed by the caller.
12130 */
12131 return fullname;
12132 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012133 if (fullname != name)
12134 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012135 }
12136
12137 /* not found in the PATH */
12138 ash_msg_and_raise_error("%s: not found", name);
12139 /* NOTREACHED */
12140}
12141
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012142static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012143dotcmd(int argc, char **argv)
12144{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012145 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012146 struct strlist *sp;
12147 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012148
12149 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012150 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012151
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012152 if (!argv[1]) {
12153 /* bash says: "bash: .: filename argument required" */
12154 return 2; /* bash compat */
12155 }
12156
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012157 /* "false; . empty_file; echo $?" should print 0, not 1: */
12158 exitstatus = 0;
12159
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012160 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012161
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012162 argv += 2;
12163 argc -= 2;
12164 if (argc) { /* argc > 0, argv[0] != NULL */
12165 saveparam = shellparam;
12166 shellparam.malloced = 0;
12167 shellparam.nparam = argc;
12168 shellparam.p = argv;
12169 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012170
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012171 setinputfile(fullname, INPUT_PUSH_FILE);
12172 commandname = fullname;
12173 cmdloop(0);
12174 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012175
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012176 if (argc) {
12177 freeparam(&shellparam);
12178 shellparam = saveparam;
12179 };
12180
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012181 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012182}
12183
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012184static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012185exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012186{
12187 if (stoppedjobs())
12188 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012189 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012190 exitstatus = number(argv[1]);
12191 raise_exception(EXEXIT);
12192 /* NOTREACHED */
12193}
12194
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012195/*
12196 * Read a file containing shell functions.
12197 */
12198static void
12199readcmdfile(char *name)
12200{
12201 setinputfile(name, INPUT_PUSH_FILE);
12202 cmdloop(0);
12203 popfile();
12204}
12205
12206
Denis Vlasenkocc571512007-02-23 21:10:35 +000012207/* ============ find_command inplementation */
12208
12209/*
12210 * Resolve a command name. If you change this routine, you may have to
12211 * change the shellexec routine as well.
12212 */
12213static void
12214find_command(char *name, struct cmdentry *entry, int act, const char *path)
12215{
12216 struct tblentry *cmdp;
12217 int idx;
12218 int prev;
12219 char *fullname;
12220 struct stat statb;
12221 int e;
12222 int updatetbl;
12223 struct builtincmd *bcmd;
12224
12225 /* If name contains a slash, don't use PATH or hash table */
12226 if (strchr(name, '/') != NULL) {
12227 entry->u.index = -1;
12228 if (act & DO_ABS) {
12229 while (stat(name, &statb) < 0) {
12230#ifdef SYSV
12231 if (errno == EINTR)
12232 continue;
12233#endif
12234 entry->cmdtype = CMDUNKNOWN;
12235 return;
12236 }
12237 }
12238 entry->cmdtype = CMDNORMAL;
12239 return;
12240 }
12241
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012242/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012243
12244 updatetbl = (path == pathval());
12245 if (!updatetbl) {
12246 act |= DO_ALTPATH;
12247 if (strstr(path, "%builtin") != NULL)
12248 act |= DO_ALTBLTIN;
12249 }
12250
12251 /* If name is in the table, check answer will be ok */
12252 cmdp = cmdlookup(name, 0);
12253 if (cmdp != NULL) {
12254 int bit;
12255
12256 switch (cmdp->cmdtype) {
12257 default:
12258#if DEBUG
12259 abort();
12260#endif
12261 case CMDNORMAL:
12262 bit = DO_ALTPATH;
12263 break;
12264 case CMDFUNCTION:
12265 bit = DO_NOFUNC;
12266 break;
12267 case CMDBUILTIN:
12268 bit = DO_ALTBLTIN;
12269 break;
12270 }
12271 if (act & bit) {
12272 updatetbl = 0;
12273 cmdp = NULL;
12274 } else if (cmdp->rehash == 0)
12275 /* if not invalidated by cd, we're done */
12276 goto success;
12277 }
12278
12279 /* If %builtin not in path, check for builtin next */
12280 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012281 if (bcmd) {
12282 if (IS_BUILTIN_REGULAR(bcmd))
12283 goto builtin_success;
12284 if (act & DO_ALTPATH) {
12285 if (!(act & DO_ALTBLTIN))
12286 goto builtin_success;
12287 } else if (builtinloc <= 0) {
12288 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012289 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012290 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012291
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012292#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012293 {
12294 int applet_no = find_applet_by_name(name);
12295 if (applet_no >= 0) {
12296 entry->cmdtype = CMDNORMAL;
12297 entry->u.index = -2 - applet_no;
12298 return;
12299 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012300 }
12301#endif
12302
Denis Vlasenkocc571512007-02-23 21:10:35 +000012303 /* We have to search path. */
12304 prev = -1; /* where to start */
12305 if (cmdp && cmdp->rehash) { /* doing a rehash */
12306 if (cmdp->cmdtype == CMDBUILTIN)
12307 prev = builtinloc;
12308 else
12309 prev = cmdp->param.index;
12310 }
12311
12312 e = ENOENT;
12313 idx = -1;
12314 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012315 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012316 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012317 /* NB: code below will still use fullname
12318 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012319 idx++;
12320 if (pathopt) {
12321 if (prefix(pathopt, "builtin")) {
12322 if (bcmd)
12323 goto builtin_success;
12324 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012325 }
12326 if ((act & DO_NOFUNC)
12327 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012328 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012329 continue;
12330 }
12331 }
12332 /* if rehash, don't redo absolute path names */
12333 if (fullname[0] == '/' && idx <= prev) {
12334 if (idx < prev)
12335 continue;
12336 TRACE(("searchexec \"%s\": no change\n", name));
12337 goto success;
12338 }
12339 while (stat(fullname, &statb) < 0) {
12340#ifdef SYSV
12341 if (errno == EINTR)
12342 continue;
12343#endif
12344 if (errno != ENOENT && errno != ENOTDIR)
12345 e = errno;
12346 goto loop;
12347 }
12348 e = EACCES; /* if we fail, this will be the error */
12349 if (!S_ISREG(statb.st_mode))
12350 continue;
12351 if (pathopt) { /* this is a %func directory */
12352 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012353 /* NB: stalloc will return space pointed by fullname
12354 * (because we don't have any intervening allocations
12355 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012356 readcmdfile(fullname);
12357 cmdp = cmdlookup(name, 0);
12358 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12359 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12360 stunalloc(fullname);
12361 goto success;
12362 }
12363 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12364 if (!updatetbl) {
12365 entry->cmdtype = CMDNORMAL;
12366 entry->u.index = idx;
12367 return;
12368 }
12369 INT_OFF;
12370 cmdp = cmdlookup(name, 1);
12371 cmdp->cmdtype = CMDNORMAL;
12372 cmdp->param.index = idx;
12373 INT_ON;
12374 goto success;
12375 }
12376
12377 /* We failed. If there was an entry for this command, delete it */
12378 if (cmdp && updatetbl)
12379 delete_cmd_entry();
12380 if (act & DO_ERR)
12381 ash_msg("%s: %s", name, errmsg(e, "not found"));
12382 entry->cmdtype = CMDUNKNOWN;
12383 return;
12384
12385 builtin_success:
12386 if (!updatetbl) {
12387 entry->cmdtype = CMDBUILTIN;
12388 entry->u.cmd = bcmd;
12389 return;
12390 }
12391 INT_OFF;
12392 cmdp = cmdlookup(name, 1);
12393 cmdp->cmdtype = CMDBUILTIN;
12394 cmdp->param.cmd = bcmd;
12395 INT_ON;
12396 success:
12397 cmdp->rehash = 0;
12398 entry->cmdtype = cmdp->cmdtype;
12399 entry->u = cmdp->param;
12400}
12401
12402
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012403/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012404
Eric Andersencb57d552001-06-28 07:25:16 +000012405/*
Eric Andersencb57d552001-06-28 07:25:16 +000012406 * The trap builtin.
12407 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012408static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012409trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012410{
12411 char *action;
12412 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012413 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012414
Eric Andersenc470f442003-07-28 09:56:35 +000012415 nextopt(nullstr);
12416 ap = argptr;
12417 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012418 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012419 char *tr = trap_ptr[signo];
12420 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012421 /* note: bash adds "SIG", but only if invoked
12422 * as "bash". If called as "sh", or if set -o posix,
12423 * then it prints short signal names.
12424 * We are printing short names: */
12425 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012426 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012427 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012428 /* trap_ptr != trap only if we are in special-cased `trap` code.
12429 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012430 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012431 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012432 }
12433 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012434 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012435 if (trap_ptr != trap) {
12436 free(trap_ptr);
12437 trap_ptr = trap;
12438 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012439 */
Eric Andersencb57d552001-06-28 07:25:16 +000012440 return 0;
12441 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012442
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012443 action = NULL;
12444 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012445 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012446 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012447 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012448 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012449 if (signo < 0) {
12450 /* Mimic bash message exactly */
12451 ash_msg("%s: invalid signal specification", *ap);
12452 exitcode = 1;
12453 goto next;
12454 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012455 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012456 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012457 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012458 action = NULL;
12459 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012460 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012461 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012462 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012463 if (action)
12464 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012465 trap[signo] = action;
12466 if (signo != 0)
12467 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012468 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012469 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012470 ap++;
12471 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012472 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012473}
12474
Eric Andersenc470f442003-07-28 09:56:35 +000012475
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012476/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012477
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012478#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012479/*
12480 * Lists available builtins
12481 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012482static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012483helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012484{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012485 unsigned col;
12486 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012487
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012488 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012489 "Built-in commands:\n"
12490 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012491 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012492 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012493 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012494 if (col > 60) {
12495 out1fmt("\n");
12496 col = 0;
12497 }
12498 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012499#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012500 {
12501 const char *a = applet_names;
12502 while (*a) {
12503 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12504 if (col > 60) {
12505 out1fmt("\n");
12506 col = 0;
12507 }
12508 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012509 }
12510 }
12511#endif
12512 out1fmt("\n\n");
12513 return EXIT_SUCCESS;
12514}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012515#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012516
Eric Andersencb57d552001-06-28 07:25:16 +000012517/*
Eric Andersencb57d552001-06-28 07:25:16 +000012518 * The export and readonly commands.
12519 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012520static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012521exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012522{
12523 struct var *vp;
12524 char *name;
12525 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012526 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012527 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012528
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012529 if (nextopt("p") != 'p') {
12530 aptr = argptr;
12531 name = *aptr;
12532 if (name) {
12533 do {
12534 p = strchr(name, '=');
12535 if (p != NULL) {
12536 p++;
12537 } else {
12538 vp = *findvar(hashvar(name), name);
12539 if (vp) {
12540 vp->flags |= flag;
12541 continue;
12542 }
Eric Andersencb57d552001-06-28 07:25:16 +000012543 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012544 setvar(name, p, flag);
12545 } while ((name = *++aptr) != NULL);
12546 return 0;
12547 }
Eric Andersencb57d552001-06-28 07:25:16 +000012548 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012549 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012550 return 0;
12551}
12552
Eric Andersencb57d552001-06-28 07:25:16 +000012553/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012554 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012555 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012556static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012557unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012558{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012559 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012560
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012561 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012562 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012563 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012564}
12565
Eric Andersencb57d552001-06-28 07:25:16 +000012566/*
Eric Andersencb57d552001-06-28 07:25:16 +000012567 * The unset builtin command. We unset the function before we unset the
12568 * variable to allow a function to be unset when there is a readonly variable
12569 * with the same name.
12570 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012571static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012572unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012573{
12574 char **ap;
12575 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012576 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012577 int ret = 0;
12578
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012579 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012580 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012581 }
Eric Andersencb57d552001-06-28 07:25:16 +000012582
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012583 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012584 if (flag != 'f') {
12585 i = unsetvar(*ap);
12586 ret |= i;
12587 if (!(i & 2))
12588 continue;
12589 }
12590 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012591 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012592 }
Eric Andersenc470f442003-07-28 09:56:35 +000012593 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012594}
12595
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012596static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012597 ' ', offsetof(struct tms, tms_utime),
12598 '\n', offsetof(struct tms, tms_stime),
12599 ' ', offsetof(struct tms, tms_cutime),
12600 '\n', offsetof(struct tms, tms_cstime),
12601 0
12602};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012603static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012604timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012605{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012606 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012607 const unsigned char *p;
12608 struct tms buf;
12609
12610 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012611 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012612
12613 p = timescmd_str;
12614 do {
12615 t = *(clock_t *)(((char *) &buf) + p[1]);
12616 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012617 t = t % clk_tck;
12618 out1fmt("%lum%lu.%03lus%c",
12619 s / 60, s % 60,
12620 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012621 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012622 p += 2;
12623 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012624
Eric Andersencb57d552001-06-28 07:25:16 +000012625 return 0;
12626}
12627
Mike Frysinger98c52642009-04-02 10:02:37 +000012628#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012629/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012630 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012631 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012632 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012633 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012634 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012635static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012636letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012637{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012638 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012639
Denis Vlasenko68404f12008-03-17 09:00:54 +000012640 argv++;
12641 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012642 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012643 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012644 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012645 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012646
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012647 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012648}
Eric Andersenc470f442003-07-28 09:56:35 +000012649#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012650
Eric Andersenc470f442003-07-28 09:56:35 +000012651/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012652 * The read builtin. Options:
12653 * -r Do not interpret '\' specially
12654 * -s Turn off echo (tty only)
12655 * -n NCHARS Read NCHARS max
12656 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12657 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12658 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012659 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012660 * TODO: bash also has:
12661 * -a ARRAY Read into array[0],[1],etc
12662 * -d DELIM End on DELIM char, not newline
12663 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012664 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012665static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012666readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012667{
Denys Vlasenko73067272010-01-12 22:11:24 +010012668 char *opt_n = NULL;
12669 char *opt_p = NULL;
12670 char *opt_t = NULL;
12671 char *opt_u = NULL;
12672 int read_flags = 0;
12673 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012674 int i;
12675
Denys Vlasenko73067272010-01-12 22:11:24 +010012676 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012677 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012678 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012679 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012680 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012681 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012682 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012683 break;
12684 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012685 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012686 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012687 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012688 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012689 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012690 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012691 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012692 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012693 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012694 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012695 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012696 default:
12697 break;
12698 }
Eric Andersenc470f442003-07-28 09:56:35 +000012699 }
Paul Fox02eb9342005-09-07 16:56:02 +000012700
Denys Vlasenko03dad222010-01-12 23:29:57 +010012701 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012702 argptr,
12703 bltinlookup("IFS"), /* can be NULL */
12704 read_flags,
12705 opt_n,
12706 opt_p,
12707 opt_t,
12708 opt_u
12709 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012710
Denys Vlasenko73067272010-01-12 22:11:24 +010012711 if ((uintptr_t)r > 1)
12712 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012713
Denys Vlasenko73067272010-01-12 22:11:24 +010012714 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012715}
12716
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012717static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012718umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012719{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012720 static const char permuser[3] ALIGN1 = "ugo";
12721 static const char permmode[3] ALIGN1 = "rwx";
12722 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012723 S_IRUSR, S_IWUSR, S_IXUSR,
12724 S_IRGRP, S_IWGRP, S_IXGRP,
12725 S_IROTH, S_IWOTH, S_IXOTH
12726 };
12727
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012728 /* TODO: use bb_parse_mode() instead */
12729
Eric Andersenc470f442003-07-28 09:56:35 +000012730 char *ap;
12731 mode_t mask;
12732 int i;
12733 int symbolic_mode = 0;
12734
12735 while (nextopt("S") != '\0') {
12736 symbolic_mode = 1;
12737 }
12738
Denis Vlasenkob012b102007-02-19 22:43:01 +000012739 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012740 mask = umask(0);
12741 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012742 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012743
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012744 ap = *argptr;
12745 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012746 if (symbolic_mode) {
12747 char buf[18];
12748 char *p = buf;
12749
12750 for (i = 0; i < 3; i++) {
12751 int j;
12752
12753 *p++ = permuser[i];
12754 *p++ = '=';
12755 for (j = 0; j < 3; j++) {
12756 if ((mask & permmask[3 * i + j]) == 0) {
12757 *p++ = permmode[j];
12758 }
12759 }
12760 *p++ = ',';
12761 }
12762 *--p = 0;
12763 puts(buf);
12764 } else {
12765 out1fmt("%.4o\n", mask);
12766 }
12767 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012768 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012769 mask = 0;
12770 do {
12771 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012772 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012773 mask = (mask << 3) + (*ap - '0');
12774 } while (*++ap != '\0');
12775 umask(mask);
12776 } else {
12777 mask = ~mask & 0777;
12778 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012779 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012780 }
12781 umask(~mask & 0777);
12782 }
12783 }
12784 return 0;
12785}
12786
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012787static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012788ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012789{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012790 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012791}
12792
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012793/* ============ main() and helpers */
12794
12795/*
12796 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012797 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012798static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012799static void
12800exitshell(void)
12801{
12802 struct jmploc loc;
12803 char *p;
12804 int status;
12805
12806 status = exitstatus;
12807 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12808 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012809 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012810/* dash bug: it just does _exit(exitstatus) here
12811 * but we have to do setjobctl(0) first!
12812 * (bug is still not fixed in dash-0.5.3 - if you run dash
12813 * under Midnight Commander, on exit from dash MC is backgrounded) */
12814 status = exitstatus;
12815 goto out;
12816 }
12817 exception_handler = &loc;
12818 p = trap[0];
12819 if (p) {
12820 trap[0] = NULL;
12821 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012822 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012823 }
12824 flush_stdout_stderr();
12825 out:
12826 setjobctl(0);
12827 _exit(status);
12828 /* NOTREACHED */
12829}
12830
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012831static void
12832init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012833{
12834 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012835 /* we will never free this */
12836 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012837
12838 /* from trap.c: */
12839 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012840 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12841 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12842 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012843 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012844
12845 /* from var.c: */
12846 {
12847 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012848 const char *p;
12849 struct stat st1, st2;
12850
12851 initvar();
12852 for (envp = environ; envp && *envp; envp++) {
12853 if (strchr(*envp, '=')) {
12854 setvareq(*envp, VEXPORT|VTEXTFIXED);
12855 }
12856 }
12857
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012858 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012859
12860 p = lookupvar("PWD");
12861 if (p)
12862 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12863 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12864 p = '\0';
12865 setpwd(p, 0);
12866 }
12867}
12868
12869/*
12870 * Process the shell command line arguments.
12871 */
12872static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012873procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012874{
12875 int i;
12876 const char *xminusc;
12877 char **xargv;
12878
12879 xargv = argv;
12880 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012881 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012882 xargv++;
12883 for (i = 0; i < NOPTS; i++)
12884 optlist[i] = 2;
12885 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012886 if (options(1)) {
12887 /* it already printed err message */
12888 raise_exception(EXERROR);
12889 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012890 xargv = argptr;
12891 xminusc = minusc;
12892 if (*xargv == NULL) {
12893 if (xminusc)
12894 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12895 sflag = 1;
12896 }
12897 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12898 iflag = 1;
12899 if (mflag == 2)
12900 mflag = iflag;
12901 for (i = 0; i < NOPTS; i++)
12902 if (optlist[i] == 2)
12903 optlist[i] = 0;
12904#if DEBUG == 2
12905 debug = 1;
12906#endif
12907 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12908 if (xminusc) {
12909 minusc = *xargv++;
12910 if (*xargv)
12911 goto setarg0;
12912 } else if (!sflag) {
12913 setinputfile(*xargv, 0);
12914 setarg0:
12915 arg0 = *xargv++;
12916 commandname = arg0;
12917 }
12918
12919 shellparam.p = xargv;
12920#if ENABLE_ASH_GETOPTS
12921 shellparam.optind = 1;
12922 shellparam.optoff = -1;
12923#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012924 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012925 while (*xargv) {
12926 shellparam.nparam++;
12927 xargv++;
12928 }
12929 optschanged();
12930}
12931
12932/*
12933 * Read /etc/profile or .profile.
12934 */
12935static void
12936read_profile(const char *name)
12937{
12938 int skip;
12939
12940 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12941 return;
12942 skip = cmdloop(0);
12943 popfile();
12944 if (skip)
12945 exitshell();
12946}
12947
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012948/*
12949 * This routine is called when an error or an interrupt occurs in an
12950 * interactive shell and control is returned to the main command loop.
12951 */
12952static void
12953reset(void)
12954{
12955 /* from eval.c: */
12956 evalskip = 0;
12957 loopnest = 0;
12958 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000012959 g_parsefile->left_in_buffer = 0;
12960 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012961 popallfiles();
12962 /* from parser.c: */
12963 tokpushback = 0;
12964 checkkwd = 0;
12965 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000012966 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012967}
12968
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012969#if PROFILE
12970static short profile_buf[16384];
12971extern int etext();
12972#endif
12973
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012974/*
12975 * Main routine. We initialize things, parse the arguments, execute
12976 * profiles if we're a login shell, and then call cmdloop to execute
12977 * commands. The setjmp call sets up the location to jump to when an
12978 * exception occurs. When an exception occurs the variable "state"
12979 * is used to figure out how far we had gotten.
12980 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012981int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012982int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012983{
Mike Frysinger98c52642009-04-02 10:02:37 +000012984 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000012985 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012986 struct jmploc jmploc;
12987 struct stackmark smark;
12988
Denis Vlasenko01631112007-12-16 17:20:38 +000012989 /* Initialize global data */
12990 INIT_G_misc();
12991 INIT_G_memstack();
12992 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012993#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012994 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012995#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012996 INIT_G_cmdtable();
12997
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012998#if PROFILE
12999 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13000#endif
13001
13002#if ENABLE_FEATURE_EDITING
13003 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13004#endif
13005 state = 0;
13006 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013007 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013008 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013009
13010 reset();
13011
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013012 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013013 if (e == EXERROR)
13014 exitstatus = 2;
13015 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013016 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013017 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013018 }
13019 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013020 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013021 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013022
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013023 popstackmark(&smark);
13024 FORCE_INT_ON; /* enable interrupts */
13025 if (s == 1)
13026 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013027 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013028 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013029 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013030 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013031 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013032 }
13033 exception_handler = &jmploc;
13034#if DEBUG
13035 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013036 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013037 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013038#endif
13039 rootpid = getpid();
13040
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013041 init();
13042 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013043 procargs(argv);
13044
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013045#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13046 if (iflag) {
13047 const char *hp = lookupvar("HISTFILE");
13048
13049 if (hp == NULL) {
13050 hp = lookupvar("HOME");
13051 if (hp != NULL) {
13052 char *defhp = concat_path_file(hp, ".ash_history");
13053 setvar("HISTFILE", defhp, 0);
13054 free(defhp);
13055 }
13056 }
13057 }
13058#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013059 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013060 isloginsh = 1;
13061 if (isloginsh) {
13062 state = 1;
13063 read_profile("/etc/profile");
13064 state1:
13065 state = 2;
13066 read_profile(".profile");
13067 }
13068 state2:
13069 state = 3;
13070 if (
13071#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013072 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013073#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013074 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013075 ) {
13076 shinit = lookupvar("ENV");
13077 if (shinit != NULL && *shinit != '\0') {
13078 read_profile(shinit);
13079 }
13080 }
13081 state3:
13082 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013083 if (minusc) {
13084 /* evalstring pushes parsefile stack.
13085 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013086 * is one of stacked source fds.
13087 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013088 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013089 // ^^ not necessary since now we special-case fd 0
13090 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013091 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013092 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013093
13094 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013095#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013096 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013097 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013098 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013099 line_input_state->hist_file = hp;
13100 }
13101#endif
13102 state4: /* XXX ??? - why isn't this before the "if" statement */
13103 cmdloop(1);
13104 }
13105#if PROFILE
13106 monitor(0);
13107#endif
13108#ifdef GPROF
13109 {
13110 extern void _mcleanup(void);
13111 _mcleanup();
13112 }
13113#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013114 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013115 exitshell();
13116 /* NOTREACHED */
13117}
13118
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013119
Eric Andersendf82f612001-06-28 07:46:40 +000013120/*-
13121 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013122 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013123 *
13124 * This code is derived from software contributed to Berkeley by
13125 * Kenneth Almquist.
13126 *
13127 * Redistribution and use in source and binary forms, with or without
13128 * modification, are permitted provided that the following conditions
13129 * are met:
13130 * 1. Redistributions of source code must retain the above copyright
13131 * notice, this list of conditions and the following disclaimer.
13132 * 2. Redistributions in binary form must reproduce the above copyright
13133 * notice, this list of conditions and the following disclaimer in the
13134 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013135 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013136 * may be used to endorse or promote products derived from this software
13137 * without specific prior written permission.
13138 *
13139 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13140 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13141 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13142 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13143 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13144 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13145 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13146 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13147 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13148 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13149 * SUCH DAMAGE.
13150 */