blob: fd119fa5155d2f25a98dd751fbbd7c20fd0f4fc3 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000016 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000039#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000040#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020043#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010044
45#include "shell_common.h"
Mike Frysinger98c52642009-04-02 10:02:37 +000046#include "math.h"
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020047#if ENABLE_ASH_RANDOM_SUPPORT
48# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020049#else
50# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020051#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000052
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020053#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010054#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000055/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020056# undef CONFIG_FEATURE_SH_STANDALONE
57# undef ENABLE_FEATURE_SH_STANDALONE
58# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010059# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020060# define ENABLE_FEATURE_SH_STANDALONE 0
61# define IF_FEATURE_SH_STANDALONE(...)
62# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000063#endif
64
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000065#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000066# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000067#endif
68
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010069#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000070# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000071#endif
72
Denys Vlasenko771f1992010-07-16 14:31:34 +020073//applet:IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP))
74//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh))
75//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
76
77//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
78//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
79
80//config:config ASH
81//config: bool "ash"
82//config: default y
83//config: depends on !NOMMU
84//config: help
85//config: Tha 'ash' shell adds about 60k in the default configuration and is
86//config: the most complete and most pedantically correct shell included with
87//config: busybox. This shell is actually a derivative of the Debian 'dash'
88//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
89//config: (written by Kenneth Almquist) from NetBSD.
90//config:
91//config:config ASH_BASH_COMPAT
92//config: bool "bash-compatible extensions"
93//config: default y
94//config: depends on ASH
95//config: help
96//config: Enable bash-compatible extensions.
97//config:
98//config:config ASH_JOB_CONTROL
99//config: bool "Job control"
100//config: default y
101//config: depends on ASH
102//config: help
103//config: Enable job control in the ash shell.
104//config:
105//config:config ASH_ALIAS
106//config: bool "alias support"
107//config: default y
108//config: depends on ASH
109//config: help
110//config: Enable alias support in the ash shell.
111//config:
112//config:config ASH_GETOPTS
113//config: bool "Builtin getopt to parse positional parameters"
114//config: default y
115//config: depends on ASH
116//config: help
117//config: Enable getopts builtin in the ash shell.
118//config:
119//config:config ASH_BUILTIN_ECHO
120//config: bool "Builtin version of 'echo'"
121//config: default y
122//config: depends on ASH
123//config: help
124//config: Enable support for echo, builtin to ash.
125//config:
126//config:config ASH_BUILTIN_PRINTF
127//config: bool "Builtin version of 'printf'"
128//config: default y
129//config: depends on ASH
130//config: help
131//config: Enable support for printf, builtin to ash.
132//config:
133//config:config ASH_BUILTIN_TEST
134//config: bool "Builtin version of 'test'"
135//config: default y
136//config: depends on ASH
137//config: help
138//config: Enable support for test, builtin to ash.
139//config:
140//config:config ASH_CMDCMD
141//config: bool "'command' command to override shell builtins"
142//config: default y
143//config: depends on ASH
144//config: help
145//config: Enable support for the ash 'command' builtin, which allows
146//config: you to run the specified command with the specified arguments,
147//config: even when there is an ash builtin command with the same name.
148//config:
149//config:config ASH_MAIL
150//config: bool "Check for new mail on interactive shells"
151//config: default n
152//config: depends on ASH
153//config: help
154//config: Enable "check for new mail" in the ash shell.
155//config:
156//config:config ASH_OPTIMIZE_FOR_SIZE
157//config: bool "Optimize for size instead of speed"
158//config: default y
159//config: depends on ASH
160//config: help
161//config: Compile ash for reduced size at the price of speed.
162//config:
163//config:config ASH_RANDOM_SUPPORT
164//config: bool "Pseudorandom generator and $RANDOM variable"
165//config: default y
166//config: depends on ASH
167//config: help
168//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
169//config: Each read of "$RANDOM" will generate a new pseudorandom value.
170//config: You can reset the generator by using a specified start value.
171//config: After "unset RANDOM" the generator will switch off and this
172//config: variable will no longer have special treatment.
173//config:
174//config:config ASH_EXPAND_PRMT
175//config: bool "Expand prompt string"
176//config: default y
177//config: depends on ASH
178//config: help
179//config: "PS#" may contain volatile content, such as backquote commands.
180//config: This option recreates the prompt string from the environment
181//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200182//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200183
184//usage:#define ash_trivial_usage NOUSAGE_STR
185//usage:#define ash_full_usage ""
186//usage:#define sh_trivial_usage NOUSAGE_STR
187//usage:#define sh_full_usage ""
188//usage:#define bash_trivial_usage NOUSAGE_STR
189//usage:#define bash_full_usage ""
190
Denis Vlasenkob012b102007-02-19 22:43:01 +0000191
Denis Vlasenko01631112007-12-16 17:20:38 +0000192/* ============ Hash table sizes. Configurable. */
193
194#define VTABSIZE 39
195#define ATABSIZE 39
196#define CMDTABLESIZE 31 /* should be prime */
197
198
Denis Vlasenkob012b102007-02-19 22:43:01 +0000199/* ============ Shell options */
200
201static const char *const optletters_optnames[] = {
202 "e" "errexit",
203 "f" "noglob",
204 "I" "ignoreeof",
205 "i" "interactive",
206 "m" "monitor",
207 "n" "noexec",
208 "s" "stdin",
209 "x" "xtrace",
210 "v" "verbose",
211 "C" "noclobber",
212 "a" "allexport",
213 "b" "notify",
214 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100215 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100216#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100217 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100218#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000219#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000220 ,"\0" "nolog"
221 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000222#endif
223};
224
Denys Vlasenko285ad152009-12-04 23:02:27 +0100225#define optletters(n) optletters_optnames[n][0]
226#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000227
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000228enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000229
Eric Andersenc470f442003-07-28 09:56:35 +0000230
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000232
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200233#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000234
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000235/*
Eric Andersenc470f442003-07-28 09:56:35 +0000236 * We enclose jmp_buf in a structure so that we can declare pointers to
237 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000238 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000239 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000240 * exception handlers, the user should save the value of handler on entry
241 * to an inner scope, set handler to point to a jmploc structure for the
242 * inner scope, and restore handler on exit from the scope.
243 */
Eric Andersenc470f442003-07-28 09:56:35 +0000244struct jmploc {
245 jmp_buf loc;
246};
Denis Vlasenko01631112007-12-16 17:20:38 +0000247
248struct globals_misc {
249 /* pid of main shell */
250 int rootpid;
251 /* shell level: 0 for the main shell, 1 for its children, and so on */
252 int shlvl;
253#define rootshell (!shlvl)
254 char *minusc; /* argument to -c option */
255
256 char *curdir; // = nullstr; /* current working directory */
257 char *physdir; // = nullstr; /* physical working directory */
258
259 char *arg0; /* value of $0 */
260
261 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000262
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200263 volatile int suppress_int; /* counter */
264 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000265 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200266 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000267 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000268 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000269#define EXINT 0 /* SIGINT received */
270#define EXERROR 1 /* a generic error */
271#define EXSHELLPROC 2 /* execute a shell procedure */
272#define EXEXEC 3 /* command execution failed */
273#define EXEXIT 4 /* exit the shell */
274#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000275
Denis Vlasenko01631112007-12-16 17:20:38 +0000276 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000277 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000278
279 char optlist[NOPTS];
280#define eflag optlist[0]
281#define fflag optlist[1]
282#define Iflag optlist[2]
283#define iflag optlist[3]
284#define mflag optlist[4]
285#define nflag optlist[5]
286#define sflag optlist[6]
287#define xflag optlist[7]
288#define vflag optlist[8]
289#define Cflag optlist[9]
290#define aflag optlist[10]
291#define bflag optlist[11]
292#define uflag optlist[12]
293#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100294#if ENABLE_ASH_BASH_COMPAT
295# define pipefail optlist[14]
296#else
297# define pipefail 0
298#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000299#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100300# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
301# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000302#endif
303
304 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000305 /*
306 * Sigmode records the current value of the signal handlers for the various
307 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000308 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000309 */
310 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000311#define S_DFL 1 /* default signal handling (SIG_DFL) */
312#define S_CATCH 2 /* signal is caught */
313#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000314#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000315
Denis Vlasenko01631112007-12-16 17:20:38 +0000316 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000317 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200318 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000319 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200320 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000321
322 /* Rarely referenced stuff */
323#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200324 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000325#endif
326 pid_t backgndpid; /* pid of last background process */
327 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000328};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000329extern struct globals_misc *const ash_ptr_to_globals_misc;
330#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000331#define rootpid (G_misc.rootpid )
332#define shlvl (G_misc.shlvl )
333#define minusc (G_misc.minusc )
334#define curdir (G_misc.curdir )
335#define physdir (G_misc.physdir )
336#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000337#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000338#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200339#define suppress_int (G_misc.suppress_int )
340#define pending_int (G_misc.pending_int )
341#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000342#define isloginsh (G_misc.isloginsh )
343#define nullstr (G_misc.nullstr )
344#define optlist (G_misc.optlist )
345#define sigmode (G_misc.sigmode )
346#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200347#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000348#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200349#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200350#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000351#define backgndpid (G_misc.backgndpid )
352#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000353#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000354 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
355 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000356 curdir = nullstr; \
357 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200358 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000359} while (0)
360
361
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000362/* ============ DEBUG */
363#if DEBUG
364static void trace_printf(const char *fmt, ...);
365static void trace_vprintf(const char *fmt, va_list va);
366# define TRACE(param) trace_printf param
367# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000368# define close(fd) do { \
369 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000370 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200371 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000372 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000373} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000374#else
375# define TRACE(param)
376# define TRACEV(param)
377#endif
378
379
Denis Vlasenko559691a2008-10-05 18:39:31 +0000380/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000381#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
382
Denis Vlasenko559691a2008-10-05 18:39:31 +0000383static int isdigit_str9(const char *str)
384{
385 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
386 while (--maxlen && isdigit(*str))
387 str++;
388 return (*str == '\0');
389}
Denis Vlasenko01631112007-12-16 17:20:38 +0000390
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200391static const char *var_end(const char *var)
392{
393 while (*var)
394 if (*var++ == '=')
395 break;
396 return var;
397}
398
Denis Vlasenko559691a2008-10-05 18:39:31 +0000399
400/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000401/*
Eric Andersen2870d962001-07-02 17:27:21 +0000402 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000403 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000404 * much more efficient and portable. (But hacking the kernel is so much
405 * more fun than worrying about efficiency and portability. :-))
406 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000407#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200408 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000409 xbarrier(); \
410} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000411
412/*
413 * Called to raise an exception. Since C doesn't include exceptions, we
414 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000415 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000416 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000417static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000418static void
419raise_exception(int e)
420{
421#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000422 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000423 abort();
424#endif
425 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000426 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000427 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000428}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000429#if DEBUG
430#define raise_exception(e) do { \
431 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
432 raise_exception(e); \
433} while (0)
434#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000435
436/*
437 * Called from trap.c when a SIGINT is received. (If the user specifies
438 * that SIGINT is to be trapped or ignored using the trap builtin, then
439 * this routine is not called.) Suppressint is nonzero when interrupts
440 * are held using the INT_OFF macro. (The test for iflag is just
441 * defensive programming.)
442 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000443static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000444static void
445raise_interrupt(void)
446{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000447 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000448
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200449 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000450 /* Signal is not automatically unmasked after it is raised,
451 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000452 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200453 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000454
Denis Vlasenko4b875702009-03-19 13:30:04 +0000455 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000456 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
457 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000458 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000459 signal(SIGINT, SIG_DFL);
460 raise(SIGINT);
461 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000462 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000463 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000464 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000465 /* NOTREACHED */
466}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000467#if DEBUG
468#define raise_interrupt() do { \
469 TRACE(("raising interrupt on line %d\n", __LINE__)); \
470 raise_interrupt(); \
471} while (0)
472#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000473
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000474static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000475int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000476{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000477 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200478 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000479 raise_interrupt();
480 }
481}
482#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000483static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000484force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000485{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000486 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200487 suppress_int = 0;
488 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000489 raise_interrupt();
490}
491#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000492
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200493#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000494
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000495#define RESTORE_INT(v) do { \
496 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200497 suppress_int = (v); \
498 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000499 raise_interrupt(); \
500} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000501
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000502
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000503/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000504
Eric Andersenc470f442003-07-28 09:56:35 +0000505static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000506outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000507{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000508 INT_OFF;
509 fputs(p, file);
510 INT_ON;
511}
512
513static void
514flush_stdout_stderr(void)
515{
516 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100517 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000518 INT_ON;
519}
520
521static void
522outcslow(int c, FILE *dest)
523{
524 INT_OFF;
525 putc(c, dest);
526 fflush(dest);
527 INT_ON;
528}
529
530static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
531static int
532out1fmt(const char *fmt, ...)
533{
534 va_list ap;
535 int r;
536
537 INT_OFF;
538 va_start(ap, fmt);
539 r = vprintf(fmt, ap);
540 va_end(ap);
541 INT_ON;
542 return r;
543}
544
545static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
546static int
547fmtstr(char *outbuf, size_t length, const char *fmt, ...)
548{
549 va_list ap;
550 int ret;
551
552 va_start(ap, fmt);
553 INT_OFF;
554 ret = vsnprintf(outbuf, length, fmt, ap);
555 va_end(ap);
556 INT_ON;
557 return ret;
558}
559
560static void
561out1str(const char *p)
562{
563 outstr(p, stdout);
564}
565
566static void
567out2str(const char *p)
568{
569 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100570 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000571}
572
573
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000574/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000575
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000576/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100577#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200578#define CTLESC ((unsigned char)'\201') /* escape next character */
579#define CTLVAR ((unsigned char)'\202') /* variable defn */
580#define CTLENDVAR ((unsigned char)'\203')
581#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000582#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
583/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200584#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
585#define CTLENDARI ((unsigned char)'\207')
586#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100587#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000588
589/* variable substitution byte (follows CTLVAR) */
590#define VSTYPE 0x0f /* type of variable substitution */
591#define VSNUL 0x10 /* colon--treat the empty string as unset */
592#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
593
594/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000595#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
596#define VSMINUS 0x2 /* ${var-text} */
597#define VSPLUS 0x3 /* ${var+text} */
598#define VSQUESTION 0x4 /* ${var?message} */
599#define VSASSIGN 0x5 /* ${var=text} */
600#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
601#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
602#define VSTRIMLEFT 0x8 /* ${var#pattern} */
603#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
604#define VSLENGTH 0xa /* ${#var} */
605#if ENABLE_ASH_BASH_COMPAT
606#define VSSUBSTR 0xc /* ${var:position:length} */
607#define VSREPLACE 0xd /* ${var/pattern/replacement} */
608#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
609#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000610
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000611static const char dolatstr[] ALIGN1 = {
612 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
613};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000614
Denis Vlasenko559691a2008-10-05 18:39:31 +0000615#define NCMD 0
616#define NPIPE 1
617#define NREDIR 2
618#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000619#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620#define NAND 5
621#define NOR 6
622#define NSEMI 7
623#define NIF 8
624#define NWHILE 9
625#define NUNTIL 10
626#define NFOR 11
627#define NCASE 12
628#define NCLIST 13
629#define NDEFUN 14
630#define NARG 15
631#define NTO 16
632#if ENABLE_ASH_BASH_COMPAT
633#define NTO2 17
634#endif
635#define NCLOBBER 18
636#define NFROM 19
637#define NFROMTO 20
638#define NAPPEND 21
639#define NTOFD 22
640#define NFROMFD 23
641#define NHERE 24
642#define NXHERE 25
643#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000644#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000645
646union node;
647
648struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000649 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000650 union node *assign;
651 union node *args;
652 union node *redirect;
653};
654
655struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000656 smallint type;
657 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000658 struct nodelist *cmdlist;
659};
660
661struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000662 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000663 union node *n;
664 union node *redirect;
665};
666
667struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000668 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000669 union node *ch1;
670 union node *ch2;
671};
672
673struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000674 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000675 union node *test;
676 union node *ifpart;
677 union node *elsepart;
678};
679
680struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000681 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000682 union node *args;
683 union node *body;
684 char *var;
685};
686
687struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000688 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000689 union node *expr;
690 union node *cases;
691};
692
693struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000694 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000695 union node *next;
696 union node *pattern;
697 union node *body;
698};
699
700struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000701 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000702 union node *next;
703 char *text;
704 struct nodelist *backquote;
705};
706
Denis Vlasenko559691a2008-10-05 18:39:31 +0000707/* nfile and ndup layout must match!
708 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
709 * that it is actually NTO2 (>&file), and change its type.
710 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000711struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000712 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000713 union node *next;
714 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000715 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000716 union node *fname;
717 char *expfname;
718};
719
720struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000721 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000722 union node *next;
723 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000724 int dupfd;
725 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000726 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000727};
728
729struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000730 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000731 union node *next;
732 int fd;
733 union node *doc;
734};
735
736struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000737 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000738 union node *com;
739};
740
741union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000742 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000743 struct ncmd ncmd;
744 struct npipe npipe;
745 struct nredir nredir;
746 struct nbinary nbinary;
747 struct nif nif;
748 struct nfor nfor;
749 struct ncase ncase;
750 struct nclist nclist;
751 struct narg narg;
752 struct nfile nfile;
753 struct ndup ndup;
754 struct nhere nhere;
755 struct nnot nnot;
756};
757
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200758/*
759 * NODE_EOF is returned by parsecmd when it encounters an end of file.
760 * It must be distinct from NULL.
761 */
762#define NODE_EOF ((union node *) -1L)
763
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000764struct nodelist {
765 struct nodelist *next;
766 union node *n;
767};
768
769struct funcnode {
770 int count;
771 union node n;
772};
773
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000774/*
775 * Free a parse tree.
776 */
777static void
778freefunc(struct funcnode *f)
779{
780 if (f && --f->count < 0)
781 free(f);
782}
783
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000784
785/* ============ Debugging output */
786
787#if DEBUG
788
789static FILE *tracefile;
790
791static void
792trace_printf(const char *fmt, ...)
793{
794 va_list va;
795
796 if (debug != 1)
797 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000798 if (DEBUG_TIME)
799 fprintf(tracefile, "%u ", (int) time(NULL));
800 if (DEBUG_PID)
801 fprintf(tracefile, "[%u] ", (int) getpid());
802 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200803 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000804 va_start(va, fmt);
805 vfprintf(tracefile, fmt, va);
806 va_end(va);
807}
808
809static void
810trace_vprintf(const char *fmt, va_list va)
811{
812 if (debug != 1)
813 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000814 if (DEBUG_TIME)
815 fprintf(tracefile, "%u ", (int) time(NULL));
816 if (DEBUG_PID)
817 fprintf(tracefile, "[%u] ", (int) getpid());
818 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200819 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000820 vfprintf(tracefile, fmt, va);
821}
822
823static void
824trace_puts(const char *s)
825{
826 if (debug != 1)
827 return;
828 fputs(s, tracefile);
829}
830
831static void
832trace_puts_quoted(char *s)
833{
834 char *p;
835 char c;
836
837 if (debug != 1)
838 return;
839 putc('"', tracefile);
840 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100841 switch ((unsigned char)*p) {
842 case '\n': c = 'n'; goto backslash;
843 case '\t': c = 't'; goto backslash;
844 case '\r': c = 'r'; goto backslash;
845 case '\"': c = '\"'; goto backslash;
846 case '\\': c = '\\'; goto backslash;
847 case CTLESC: c = 'e'; goto backslash;
848 case CTLVAR: c = 'v'; goto backslash;
849 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
850 case CTLBACKQ: c = 'q'; goto backslash;
851 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000852 backslash:
853 putc('\\', tracefile);
854 putc(c, tracefile);
855 break;
856 default:
857 if (*p >= ' ' && *p <= '~')
858 putc(*p, tracefile);
859 else {
860 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100861 putc((*p >> 6) & 03, tracefile);
862 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000863 putc(*p & 07, tracefile);
864 }
865 break;
866 }
867 }
868 putc('"', tracefile);
869}
870
871static void
872trace_puts_args(char **ap)
873{
874 if (debug != 1)
875 return;
876 if (!*ap)
877 return;
878 while (1) {
879 trace_puts_quoted(*ap);
880 if (!*++ap) {
881 putc('\n', tracefile);
882 break;
883 }
884 putc(' ', tracefile);
885 }
886}
887
888static void
889opentrace(void)
890{
891 char s[100];
892#ifdef O_APPEND
893 int flags;
894#endif
895
896 if (debug != 1) {
897 if (tracefile)
898 fflush(tracefile);
899 /* leave open because libedit might be using it */
900 return;
901 }
902 strcpy(s, "./trace");
903 if (tracefile) {
904 if (!freopen(s, "a", tracefile)) {
905 fprintf(stderr, "Can't re-open %s\n", s);
906 debug = 0;
907 return;
908 }
909 } else {
910 tracefile = fopen(s, "a");
911 if (tracefile == NULL) {
912 fprintf(stderr, "Can't open %s\n", s);
913 debug = 0;
914 return;
915 }
916 }
917#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000918 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000919 if (flags >= 0)
920 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
921#endif
922 setlinebuf(tracefile);
923 fputs("\nTracing started.\n", tracefile);
924}
925
926static void
927indent(int amount, char *pfx, FILE *fp)
928{
929 int i;
930
931 for (i = 0; i < amount; i++) {
932 if (pfx && i == amount - 1)
933 fputs(pfx, fp);
934 putc('\t', fp);
935 }
936}
937
938/* little circular references here... */
939static void shtree(union node *n, int ind, char *pfx, FILE *fp);
940
941static void
942sharg(union node *arg, FILE *fp)
943{
944 char *p;
945 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100946 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000947
948 if (arg->type != NARG) {
949 out1fmt("<node type %d>\n", arg->type);
950 abort();
951 }
952 bqlist = arg->narg.backquote;
953 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100954 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000955 case CTLESC:
956 putc(*++p, fp);
957 break;
958 case CTLVAR:
959 putc('$', fp);
960 putc('{', fp);
961 subtype = *++p;
962 if (subtype == VSLENGTH)
963 putc('#', fp);
964
965 while (*p != '=')
966 putc(*p++, fp);
967
968 if (subtype & VSNUL)
969 putc(':', fp);
970
971 switch (subtype & VSTYPE) {
972 case VSNORMAL:
973 putc('}', fp);
974 break;
975 case VSMINUS:
976 putc('-', fp);
977 break;
978 case VSPLUS:
979 putc('+', fp);
980 break;
981 case VSQUESTION:
982 putc('?', fp);
983 break;
984 case VSASSIGN:
985 putc('=', fp);
986 break;
987 case VSTRIMLEFT:
988 putc('#', fp);
989 break;
990 case VSTRIMLEFTMAX:
991 putc('#', fp);
992 putc('#', fp);
993 break;
994 case VSTRIMRIGHT:
995 putc('%', fp);
996 break;
997 case VSTRIMRIGHTMAX:
998 putc('%', fp);
999 putc('%', fp);
1000 break;
1001 case VSLENGTH:
1002 break;
1003 default:
1004 out1fmt("<subtype %d>", subtype);
1005 }
1006 break;
1007 case CTLENDVAR:
1008 putc('}', fp);
1009 break;
1010 case CTLBACKQ:
1011 case CTLBACKQ|CTLQUOTE:
1012 putc('$', fp);
1013 putc('(', fp);
1014 shtree(bqlist->n, -1, NULL, fp);
1015 putc(')', fp);
1016 break;
1017 default:
1018 putc(*p, fp);
1019 break;
1020 }
1021 }
1022}
1023
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001024static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001025shcmd(union node *cmd, FILE *fp)
1026{
1027 union node *np;
1028 int first;
1029 const char *s;
1030 int dftfd;
1031
1032 first = 1;
1033 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001034 if (!first)
1035 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001036 sharg(np, fp);
1037 first = 0;
1038 }
1039 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001040 if (!first)
1041 putc(' ', fp);
1042 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001043 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001044 case NTO: s = ">>"+1; dftfd = 1; break;
1045 case NCLOBBER: s = ">|"; dftfd = 1; break;
1046 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001047#if ENABLE_ASH_BASH_COMPAT
1048 case NTO2:
1049#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001050 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001051 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001052 case NFROMFD: s = "<&"; break;
1053 case NFROMTO: s = "<>"; break;
1054 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001055 }
1056 if (np->nfile.fd != dftfd)
1057 fprintf(fp, "%d", np->nfile.fd);
1058 fputs(s, fp);
1059 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1060 fprintf(fp, "%d", np->ndup.dupfd);
1061 } else {
1062 sharg(np->nfile.fname, fp);
1063 }
1064 first = 0;
1065 }
1066}
1067
1068static void
1069shtree(union node *n, int ind, char *pfx, FILE *fp)
1070{
1071 struct nodelist *lp;
1072 const char *s;
1073
1074 if (n == NULL)
1075 return;
1076
1077 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001078
1079 if (n == NODE_EOF) {
1080 fputs("<EOF>", fp);
1081 return;
1082 }
1083
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001084 switch (n->type) {
1085 case NSEMI:
1086 s = "; ";
1087 goto binop;
1088 case NAND:
1089 s = " && ";
1090 goto binop;
1091 case NOR:
1092 s = " || ";
1093 binop:
1094 shtree(n->nbinary.ch1, ind, NULL, fp);
1095 /* if (ind < 0) */
1096 fputs(s, fp);
1097 shtree(n->nbinary.ch2, ind, NULL, fp);
1098 break;
1099 case NCMD:
1100 shcmd(n, fp);
1101 if (ind >= 0)
1102 putc('\n', fp);
1103 break;
1104 case NPIPE:
1105 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001106 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001107 if (lp->next)
1108 fputs(" | ", fp);
1109 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001110 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001111 fputs(" &", fp);
1112 if (ind >= 0)
1113 putc('\n', fp);
1114 break;
1115 default:
1116 fprintf(fp, "<node type %d>", n->type);
1117 if (ind >= 0)
1118 putc('\n', fp);
1119 break;
1120 }
1121}
1122
1123static void
1124showtree(union node *n)
1125{
1126 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001127 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001128}
1129
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001130#endif /* DEBUG */
1131
1132
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001133/* ============ Parser data */
1134
1135/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001136 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1137 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001138struct strlist {
1139 struct strlist *next;
1140 char *text;
1141};
1142
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001143struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001144
Denis Vlasenkob012b102007-02-19 22:43:01 +00001145struct strpush {
1146 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001147 char *prev_string;
1148 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001149#if ENABLE_ASH_ALIAS
1150 struct alias *ap; /* if push was associated with an alias */
1151#endif
1152 char *string; /* remember the string since it may change */
1153};
1154
1155struct parsefile {
1156 struct parsefile *prev; /* preceding file on stack */
1157 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001158 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001159 int left_in_line; /* number of chars left in this line */
1160 int left_in_buffer; /* number of chars left in this buffer past the line */
1161 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001162 char *buf; /* input buffer */
1163 struct strpush *strpush; /* for pushing strings at this level */
1164 struct strpush basestrpush; /* so pushing one is fast */
1165};
1166
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001167static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001168static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001169static int startlinno; /* line # where last token started */
1170static char *commandname; /* currently executing command */
1171static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001172static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001173
1174
1175/* ============ Message printing */
1176
1177static void
1178ash_vmsg(const char *msg, va_list ap)
1179{
1180 fprintf(stderr, "%s: ", arg0);
1181 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001182 if (strcmp(arg0, commandname))
1183 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001184 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001185 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001186 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001187 vfprintf(stderr, msg, ap);
1188 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001189}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001190
1191/*
1192 * Exverror is called to raise the error exception. If the second argument
1193 * is not NULL then error prints an error message using printf style
1194 * formatting. It then raises the error exception.
1195 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001196static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001197static void
1198ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001199{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001200#if DEBUG
1201 if (msg) {
1202 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1203 TRACEV((msg, ap));
1204 TRACE(("\") pid=%d\n", getpid()));
1205 } else
1206 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1207 if (msg)
1208#endif
1209 ash_vmsg(msg, ap);
1210
1211 flush_stdout_stderr();
1212 raise_exception(cond);
1213 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001214}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001215
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001216static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001217static void
1218ash_msg_and_raise_error(const char *msg, ...)
1219{
1220 va_list ap;
1221
1222 va_start(ap, msg);
1223 ash_vmsg_and_raise(EXERROR, msg, ap);
1224 /* NOTREACHED */
1225 va_end(ap);
1226}
1227
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001228static void raise_error_syntax(const char *) NORETURN;
1229static void
1230raise_error_syntax(const char *msg)
1231{
1232 ash_msg_and_raise_error("syntax error: %s", msg);
1233 /* NOTREACHED */
1234}
1235
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001236static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001237static void
1238ash_msg_and_raise(int cond, const char *msg, ...)
1239{
1240 va_list ap;
1241
1242 va_start(ap, msg);
1243 ash_vmsg_and_raise(cond, msg, ap);
1244 /* NOTREACHED */
1245 va_end(ap);
1246}
1247
1248/*
1249 * error/warning routines for external builtins
1250 */
1251static void
1252ash_msg(const char *fmt, ...)
1253{
1254 va_list ap;
1255
1256 va_start(ap, fmt);
1257 ash_vmsg(fmt, ap);
1258 va_end(ap);
1259}
1260
1261/*
1262 * Return a string describing an error. The returned string may be a
1263 * pointer to a static buffer that will be overwritten on the next call.
1264 * Action describes the operation that got the error.
1265 */
1266static const char *
1267errmsg(int e, const char *em)
1268{
1269 if (e == ENOENT || e == ENOTDIR) {
1270 return em;
1271 }
1272 return strerror(e);
1273}
1274
1275
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001276/* ============ Memory allocation */
1277
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001278#if 0
1279/* I consider these wrappers nearly useless:
1280 * ok, they return you to nearest exception handler, but
1281 * how much memory do you leak in the process, making
1282 * memory starvation worse?
1283 */
1284static void *
1285ckrealloc(void * p, size_t nbytes)
1286{
1287 p = realloc(p, nbytes);
1288 if (!p)
1289 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1290 return p;
1291}
1292
1293static void *
1294ckmalloc(size_t nbytes)
1295{
1296 return ckrealloc(NULL, nbytes);
1297}
1298
1299static void *
1300ckzalloc(size_t nbytes)
1301{
1302 return memset(ckmalloc(nbytes), 0, nbytes);
1303}
1304
1305static char *
1306ckstrdup(const char *s)
1307{
1308 char *p = strdup(s);
1309 if (!p)
1310 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1311 return p;
1312}
1313#else
1314/* Using bbox equivalents. They exit if out of memory */
1315# define ckrealloc xrealloc
1316# define ckmalloc xmalloc
1317# define ckzalloc xzalloc
1318# define ckstrdup xstrdup
1319#endif
1320
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001321/*
1322 * It appears that grabstackstr() will barf with such alignments
1323 * because stalloc() will return a string allocated in a new stackblock.
1324 */
1325#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1326enum {
1327 /* Most machines require the value returned from malloc to be aligned
1328 * in some way. The following macro will get this right
1329 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001330 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001332 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001333};
1334
1335struct stack_block {
1336 struct stack_block *prev;
1337 char space[MINSIZE];
1338};
1339
1340struct stackmark {
1341 struct stack_block *stackp;
1342 char *stacknxt;
1343 size_t stacknleft;
1344 struct stackmark *marknext;
1345};
1346
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347
Denis Vlasenko01631112007-12-16 17:20:38 +00001348struct globals_memstack {
1349 struct stack_block *g_stackp; // = &stackbase;
1350 struct stackmark *markp;
1351 char *g_stacknxt; // = stackbase.space;
1352 char *sstrend; // = stackbase.space + MINSIZE;
1353 size_t g_stacknleft; // = MINSIZE;
1354 int herefd; // = -1;
1355 struct stack_block stackbase;
1356};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001357extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1358#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001359#define g_stackp (G_memstack.g_stackp )
1360#define markp (G_memstack.markp )
1361#define g_stacknxt (G_memstack.g_stacknxt )
1362#define sstrend (G_memstack.sstrend )
1363#define g_stacknleft (G_memstack.g_stacknleft)
1364#define herefd (G_memstack.herefd )
1365#define stackbase (G_memstack.stackbase )
1366#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001367 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1368 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001369 g_stackp = &stackbase; \
1370 g_stacknxt = stackbase.space; \
1371 g_stacknleft = MINSIZE; \
1372 sstrend = stackbase.space + MINSIZE; \
1373 herefd = -1; \
1374} while (0)
1375
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001376
Denis Vlasenko01631112007-12-16 17:20:38 +00001377#define stackblock() ((void *)g_stacknxt)
1378#define stackblocksize() g_stacknleft
1379
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001380/*
1381 * Parse trees for commands are allocated in lifo order, so we use a stack
1382 * to make this more efficient, and also to avoid all sorts of exception
1383 * handling code to handle interrupts in the middle of a parse.
1384 *
1385 * The size 504 was chosen because the Ultrix malloc handles that size
1386 * well.
1387 */
1388static void *
1389stalloc(size_t nbytes)
1390{
1391 char *p;
1392 size_t aligned;
1393
1394 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001395 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001396 size_t len;
1397 size_t blocksize;
1398 struct stack_block *sp;
1399
1400 blocksize = aligned;
1401 if (blocksize < MINSIZE)
1402 blocksize = MINSIZE;
1403 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1404 if (len < blocksize)
1405 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1406 INT_OFF;
1407 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001408 sp->prev = g_stackp;
1409 g_stacknxt = sp->space;
1410 g_stacknleft = blocksize;
1411 sstrend = g_stacknxt + blocksize;
1412 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001413 INT_ON;
1414 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001415 p = g_stacknxt;
1416 g_stacknxt += aligned;
1417 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001418 return p;
1419}
1420
Denis Vlasenko597906c2008-02-20 16:38:54 +00001421static void *
1422stzalloc(size_t nbytes)
1423{
1424 return memset(stalloc(nbytes), 0, nbytes);
1425}
1426
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001427static void
1428stunalloc(void *p)
1429{
1430#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001431 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001432 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001433 abort();
1434 }
1435#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001436 g_stacknleft += g_stacknxt - (char *)p;
1437 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001438}
1439
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001440/*
1441 * Like strdup but works with the ash stack.
1442 */
1443static char *
1444ststrdup(const char *p)
1445{
1446 size_t len = strlen(p) + 1;
1447 return memcpy(stalloc(len), p, len);
1448}
1449
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001450static void
1451setstackmark(struct stackmark *mark)
1452{
Denis Vlasenko01631112007-12-16 17:20:38 +00001453 mark->stackp = g_stackp;
1454 mark->stacknxt = g_stacknxt;
1455 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001456 mark->marknext = markp;
1457 markp = mark;
1458}
1459
1460static void
1461popstackmark(struct stackmark *mark)
1462{
1463 struct stack_block *sp;
1464
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001465 if (!mark->stackp)
1466 return;
1467
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001468 INT_OFF;
1469 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001470 while (g_stackp != mark->stackp) {
1471 sp = g_stackp;
1472 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001473 free(sp);
1474 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001475 g_stacknxt = mark->stacknxt;
1476 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001477 sstrend = mark->stacknxt + mark->stacknleft;
1478 INT_ON;
1479}
1480
1481/*
1482 * When the parser reads in a string, it wants to stick the string on the
1483 * stack and only adjust the stack pointer when it knows how big the
1484 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1485 * of space on top of the stack and stackblocklen returns the length of
1486 * this block. Growstackblock will grow this space by at least one byte,
1487 * possibly moving it (like realloc). Grabstackblock actually allocates the
1488 * part of the block that has been used.
1489 */
1490static void
1491growstackblock(void)
1492{
1493 size_t newlen;
1494
Denis Vlasenko01631112007-12-16 17:20:38 +00001495 newlen = g_stacknleft * 2;
1496 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001497 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1498 if (newlen < 128)
1499 newlen += 128;
1500
Denis Vlasenko01631112007-12-16 17:20:38 +00001501 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001502 struct stack_block *oldstackp;
1503 struct stackmark *xmark;
1504 struct stack_block *sp;
1505 struct stack_block *prevstackp;
1506 size_t grosslen;
1507
1508 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001509 oldstackp = g_stackp;
1510 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001511 prevstackp = sp->prev;
1512 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1513 sp = ckrealloc(sp, grosslen);
1514 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001515 g_stackp = sp;
1516 g_stacknxt = sp->space;
1517 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001518 sstrend = sp->space + newlen;
1519
1520 /*
1521 * Stack marks pointing to the start of the old block
1522 * must be relocated to point to the new block
1523 */
1524 xmark = markp;
1525 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001526 xmark->stackp = g_stackp;
1527 xmark->stacknxt = g_stacknxt;
1528 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001529 xmark = xmark->marknext;
1530 }
1531 INT_ON;
1532 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001533 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001534 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001535 char *p = stalloc(newlen);
1536
1537 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001538 g_stacknxt = memcpy(p, oldspace, oldlen);
1539 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001540 }
1541}
1542
1543static void
1544grabstackblock(size_t len)
1545{
1546 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001547 g_stacknxt += len;
1548 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001549}
1550
1551/*
1552 * The following routines are somewhat easier to use than the above.
1553 * The user declares a variable of type STACKSTR, which may be declared
1554 * to be a register. The macro STARTSTACKSTR initializes things. Then
1555 * the user uses the macro STPUTC to add characters to the string. In
1556 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1557 * grown as necessary. When the user is done, she can just leave the
1558 * string there and refer to it using stackblock(). Or she can allocate
1559 * the space for it using grabstackstr(). If it is necessary to allow
1560 * someone else to use the stack temporarily and then continue to grow
1561 * the string, the user should use grabstack to allocate the space, and
1562 * then call ungrabstr(p) to return to the previous mode of operation.
1563 *
1564 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1565 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1566 * is space for at least one character.
1567 */
1568static void *
1569growstackstr(void)
1570{
1571 size_t len = stackblocksize();
1572 if (herefd >= 0 && len >= 1024) {
1573 full_write(herefd, stackblock(), len);
1574 return stackblock();
1575 }
1576 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001577 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001578}
1579
1580/*
1581 * Called from CHECKSTRSPACE.
1582 */
1583static char *
1584makestrspace(size_t newlen, char *p)
1585{
Denis Vlasenko01631112007-12-16 17:20:38 +00001586 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001587 size_t size = stackblocksize();
1588
1589 for (;;) {
1590 size_t nleft;
1591
1592 size = stackblocksize();
1593 nleft = size - len;
1594 if (nleft >= newlen)
1595 break;
1596 growstackblock();
1597 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001598 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001599}
1600
1601static char *
1602stack_nputstr(const char *s, size_t n, char *p)
1603{
1604 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001605 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001606 return p;
1607}
1608
1609static char *
1610stack_putstr(const char *s, char *p)
1611{
1612 return stack_nputstr(s, strlen(s), p);
1613}
1614
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001615static char *
1616_STPUTC(int c, char *p)
1617{
1618 if (p == sstrend)
1619 p = growstackstr();
1620 *p++ = c;
1621 return p;
1622}
1623
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001624#define STARTSTACKSTR(p) ((p) = stackblock())
1625#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001626#define CHECKSTRSPACE(n, p) do { \
1627 char *q = (p); \
1628 size_t l = (n); \
1629 size_t m = sstrend - q; \
1630 if (l > m) \
1631 (p) = makestrspace(l, q); \
1632} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001633#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001634#define STACKSTRNUL(p) do { \
1635 if ((p) == sstrend) \
1636 (p) = growstackstr(); \
1637 *(p) = '\0'; \
1638} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001639#define STUNPUTC(p) (--(p))
1640#define STTOPC(p) ((p)[-1])
1641#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001642
1643#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001644#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001645#define stackstrend() ((void *)sstrend)
1646
1647
1648/* ============ String helpers */
1649
1650/*
1651 * prefix -- see if pfx is a prefix of string.
1652 */
1653static char *
1654prefix(const char *string, const char *pfx)
1655{
1656 while (*pfx) {
1657 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001658 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001659 }
1660 return (char *) string;
1661}
1662
1663/*
1664 * Check for a valid number. This should be elsewhere.
1665 */
1666static int
1667is_number(const char *p)
1668{
1669 do {
1670 if (!isdigit(*p))
1671 return 0;
1672 } while (*++p != '\0');
1673 return 1;
1674}
1675
1676/*
1677 * Convert a string of digits to an integer, printing an error message on
1678 * failure.
1679 */
1680static int
1681number(const char *s)
1682{
1683 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001684 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001685 return atoi(s);
1686}
1687
1688/*
1689 * Produce a possibly single quoted string suitable as input to the shell.
1690 * The return string is allocated on the stack.
1691 */
1692static char *
1693single_quote(const char *s)
1694{
1695 char *p;
1696
1697 STARTSTACKSTR(p);
1698
1699 do {
1700 char *q;
1701 size_t len;
1702
1703 len = strchrnul(s, '\'') - s;
1704
1705 q = p = makestrspace(len + 3, p);
1706
1707 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001708 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001709 *q++ = '\'';
1710 s += len;
1711
1712 STADJUST(q - p, p);
1713
Denys Vlasenkocd716832009-11-28 22:14:02 +01001714 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001716 len = 0;
1717 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001718
1719 q = p = makestrspace(len + 3, p);
1720
1721 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001722 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001724
1725 STADJUST(q - p, p);
1726 } while (*s);
1727
Denys Vlasenkocd716832009-11-28 22:14:02 +01001728 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729
1730 return stackblock();
1731}
1732
1733
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001734/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735
1736static char **argptr; /* argument list for builtin commands */
1737static char *optionarg; /* set by nextopt (like getopt) */
1738static char *optptr; /* used by nextopt */
1739
1740/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001741 * XXX - should get rid of. Have all builtins use getopt(3).
1742 * The library getopt must have the BSD extension static variable
1743 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001745 * Standard option processing (a la getopt) for builtin routines.
1746 * The only argument that is passed to nextopt is the option string;
1747 * the other arguments are unnecessary. It returns the character,
1748 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749 */
1750static int
1751nextopt(const char *optstring)
1752{
1753 char *p;
1754 const char *q;
1755 char c;
1756
1757 p = optptr;
1758 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001759 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001761 if (p == NULL)
1762 return '\0';
1763 if (*p != '-')
1764 return '\0';
1765 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001766 return '\0';
1767 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001768 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001770 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001771 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001772 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001773 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001774 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001775 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001776 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001777 if (*++q == ':')
1778 q++;
1779 }
1780 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001781 if (*p == '\0') {
1782 p = *argptr++;
1783 if (p == NULL)
1784 ash_msg_and_raise_error("no arg for -%c option", c);
1785 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786 optionarg = p;
1787 p = NULL;
1788 }
1789 optptr = p;
1790 return c;
1791}
1792
1793
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001794/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001795
Denis Vlasenko01631112007-12-16 17:20:38 +00001796/*
1797 * The parsefile structure pointed to by the global variable parsefile
1798 * contains information about the current file being read.
1799 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001800struct shparam {
1801 int nparam; /* # of positional parameters (without $0) */
1802#if ENABLE_ASH_GETOPTS
1803 int optind; /* next parameter to be processed by getopts */
1804 int optoff; /* used by getopts */
1805#endif
1806 unsigned char malloced; /* if parameter list dynamically allocated */
1807 char **p; /* parameter list */
1808};
1809
1810/*
1811 * Free the list of positional parameters.
1812 */
1813static void
1814freeparam(volatile struct shparam *param)
1815{
Denis Vlasenko01631112007-12-16 17:20:38 +00001816 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001817 char **ap, **ap1;
1818 ap = ap1 = param->p;
1819 while (*ap)
1820 free(*ap++);
1821 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001822 }
1823}
1824
1825#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001826static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001827#endif
1828
1829struct var {
1830 struct var *next; /* next entry in hash list */
1831 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001832 const char *var_text; /* name=value */
1833 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001834 /* the variable gets set/unset */
1835};
1836
1837struct localvar {
1838 struct localvar *next; /* next local variable in list */
1839 struct var *vp; /* the variable that was made local */
1840 int flags; /* saved flags */
1841 const char *text; /* saved text */
1842};
1843
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844/* flags */
1845#define VEXPORT 0x01 /* variable is exported */
1846#define VREADONLY 0x02 /* variable cannot be modified */
1847#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1848#define VTEXTFIXED 0x08 /* text is statically allocated */
1849#define VSTACK 0x10 /* text is allocated on the stack */
1850#define VUNSET 0x20 /* the variable is not set */
1851#define VNOFUNC 0x40 /* don't call the callback function */
1852#define VNOSET 0x80 /* do not set variable - just readonly test */
1853#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001854#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001855# define VDYNAMIC 0x200 /* dynamic variable */
1856#else
1857# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001858#endif
1859
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001860
Denis Vlasenko01631112007-12-16 17:20:38 +00001861/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001862#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001863static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001864change_lc_all(const char *value)
1865{
1866 if (value && *value != '\0')
1867 setlocale(LC_ALL, value);
1868}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001869static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001870change_lc_ctype(const char *value)
1871{
1872 if (value && *value != '\0')
1873 setlocale(LC_CTYPE, value);
1874}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001875#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001876#if ENABLE_ASH_MAIL
1877static void chkmail(void);
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001878static void changemail(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001879#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001880static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001881#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001882static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001883#endif
1884
Denis Vlasenko01631112007-12-16 17:20:38 +00001885static const struct {
1886 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001887 const char *var_text;
1888 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001889} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001890 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001892 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1893 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001894#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001895 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1896 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1897 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1898 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001899#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001900 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001901#endif
1902#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001903 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001904#endif
1905#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001906 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1907 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001908#endif
1909#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001910 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
1912};
1913
Denis Vlasenko0b769642008-07-24 07:54:57 +00001914struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001915
1916struct globals_var {
1917 struct shparam shellparam; /* $@ current positional parameters */
1918 struct redirtab *redirlist;
1919 int g_nullredirs;
1920 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1921 struct var *vartab[VTABSIZE];
1922 struct var varinit[ARRAY_SIZE(varinit_data)];
1923};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001924extern struct globals_var *const ash_ptr_to_globals_var;
1925#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001926#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001927//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001928#define g_nullredirs (G_var.g_nullredirs )
1929#define preverrout_fd (G_var.preverrout_fd)
1930#define vartab (G_var.vartab )
1931#define varinit (G_var.varinit )
1932#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001933 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001934 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1935 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001936 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001937 varinit[i].flags = varinit_data[i].flags; \
1938 varinit[i].var_text = varinit_data[i].var_text; \
1939 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001940 } \
1941} while (0)
1942
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001943#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001944#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001945# define vmail (&vifs)[1]
1946# define vmpath (&vmail)[1]
1947# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001948#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001949# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001950#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001951#define vps1 (&vpath)[1]
1952#define vps2 (&vps1)[1]
1953#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001954#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001955# define voptind (&vps4)[1]
1956# if ENABLE_ASH_RANDOM_SUPPORT
1957# define vrandom (&voptind)[1]
1958# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001959#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001960# if ENABLE_ASH_RANDOM_SUPPORT
1961# define vrandom (&vps4)[1]
1962# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001963#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964
1965/*
1966 * The following macros access the values of the above variables.
1967 * They have to skip over the name. They return the null string
1968 * for unset variables.
1969 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001970#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001971#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001972#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001973# define mailval() (vmail.var_text + 5)
1974# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001975# define mpathset() ((vmpath.flags & VUNSET) == 0)
1976#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001977#define pathval() (vpath.var_text + 5)
1978#define ps1val() (vps1.var_text + 4)
1979#define ps2val() (vps2.var_text + 4)
1980#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001981#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001982# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001983#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001985
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001986#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1987#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1988
Denis Vlasenko01631112007-12-16 17:20:38 +00001989#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001990static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001991getoptsreset(const char *value)
1992{
1993 shellparam.optind = number(value);
1994 shellparam.optoff = -1;
1995}
1996#endif
1997
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001998/*
1999 * Return of a legal variable name (a letter or underscore followed by zero or
2000 * more letters, underscores, and digits).
2001 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002002static char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002003endofname(const char *name)
2004{
2005 char *p;
2006
2007 p = (char *) name;
2008 if (!is_name(*p))
2009 return p;
2010 while (*++p) {
2011 if (!is_in_name(*p))
2012 break;
2013 }
2014 return p;
2015}
2016
2017/*
2018 * Compares two strings up to the first = or '\0'. The first
2019 * string must be terminated by '='; the second may be terminated by
2020 * either '=' or '\0'.
2021 */
2022static int
2023varcmp(const char *p, const char *q)
2024{
2025 int c, d;
2026
2027 while ((c = *p) == (d = *q)) {
2028 if (!c || c == '=')
2029 goto out;
2030 p++;
2031 q++;
2032 }
2033 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002034 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002035 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002036 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002037 out:
2038 return c - d;
2039}
2040
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002041/*
2042 * Find the appropriate entry in the hash table from the name.
2043 */
2044static struct var **
2045hashvar(const char *p)
2046{
2047 unsigned hashval;
2048
2049 hashval = ((unsigned char) *p) << 4;
2050 while (*p && *p != '=')
2051 hashval += (unsigned char) *p++;
2052 return &vartab[hashval % VTABSIZE];
2053}
2054
2055static int
2056vpcmp(const void *a, const void *b)
2057{
2058 return varcmp(*(const char **)a, *(const char **)b);
2059}
2060
2061/*
2062 * This routine initializes the builtin variables.
2063 */
2064static void
2065initvar(void)
2066{
2067 struct var *vp;
2068 struct var *end;
2069 struct var **vpp;
2070
2071 /*
2072 * PS1 depends on uid
2073 */
2074#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002075 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002076#else
2077 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002078 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079#endif
2080 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002081 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002082 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002083 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002084 vp->next = *vpp;
2085 *vpp = vp;
2086 } while (++vp < end);
2087}
2088
2089static struct var **
2090findvar(struct var **vpp, const char *name)
2091{
2092 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002093 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094 break;
2095 }
2096 }
2097 return vpp;
2098}
2099
2100/*
2101 * Find the value of a variable. Returns NULL if not set.
2102 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002103static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002104lookupvar(const char *name)
2105{
2106 struct var *v;
2107
2108 v = *findvar(hashvar(name), name);
2109 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002110#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002111 /*
2112 * Dynamic variables are implemented roughly the same way they are
2113 * in bash. Namely, they're "special" so long as they aren't unset.
2114 * As soon as they're unset, they're no longer dynamic, and dynamic
2115 * lookup will no longer happen at that point. -- PFM.
2116 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002117 if (v->flags & VDYNAMIC)
2118 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119#endif
2120 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002121 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002122 }
2123 return NULL;
2124}
2125
2126/*
2127 * Search the environment of a builtin command.
2128 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002129static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002130bltinlookup(const char *name)
2131{
2132 struct strlist *sp;
2133
2134 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002135 if (varcmp(sp->text, name) == 0)
2136 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002137 }
2138 return lookupvar(name);
2139}
2140
2141/*
2142 * Same as setvar except that the variable and value are passed in
2143 * the first argument as name=value. Since the first argument will
2144 * be actually stored in the table, it should not be a string that
2145 * will go away.
2146 * Called with interrupts off.
2147 */
2148static void
2149setvareq(char *s, int flags)
2150{
2151 struct var *vp, **vpp;
2152
2153 vpp = hashvar(s);
2154 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2155 vp = *findvar(vpp, s);
2156 if (vp) {
2157 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2158 const char *n;
2159
2160 if (flags & VNOSAVE)
2161 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002162 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002163 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2164 }
2165
2166 if (flags & VNOSET)
2167 return;
2168
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002169 if (vp->var_func && !(flags & VNOFUNC))
2170 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002171
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002172 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2173 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002174
2175 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2176 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002177 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002178 if (flags & VNOSET)
2179 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002180 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002181 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002182 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002183 *vpp = vp;
2184 }
2185 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2186 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002187 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002188 vp->flags = flags;
2189}
2190
2191/*
2192 * Set the value of a variable. The flags argument is ored with the
2193 * flags of the variable. If val is NULL, the variable is unset.
2194 */
2195static void
2196setvar(const char *name, const char *val, int flags)
2197{
2198 char *p, *q;
2199 size_t namelen;
2200 char *nameeq;
2201 size_t vallen;
2202
2203 q = endofname(name);
2204 p = strchrnul(q, '=');
2205 namelen = p - name;
2206 if (!namelen || p != q)
2207 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2208 vallen = 0;
2209 if (val == NULL) {
2210 flags |= VUNSET;
2211 } else {
2212 vallen = strlen(val);
2213 }
2214 INT_OFF;
2215 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002216 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002217 if (val) {
2218 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002219 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002220 }
2221 *p = '\0';
2222 setvareq(nameeq, flags | VNOSAVE);
2223 INT_ON;
2224}
2225
Denys Vlasenko03dad222010-01-12 23:29:57 +01002226static void FAST_FUNC
2227setvar2(const char *name, const char *val)
2228{
2229 setvar(name, val, 0);
2230}
2231
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002232#if ENABLE_ASH_GETOPTS
2233/*
2234 * Safe version of setvar, returns 1 on success 0 on failure.
2235 */
2236static int
2237setvarsafe(const char *name, const char *val, int flags)
2238{
2239 int err;
2240 volatile int saveint;
2241 struct jmploc *volatile savehandler = exception_handler;
2242 struct jmploc jmploc;
2243
2244 SAVE_INT(saveint);
2245 if (setjmp(jmploc.loc))
2246 err = 1;
2247 else {
2248 exception_handler = &jmploc;
2249 setvar(name, val, flags);
2250 err = 0;
2251 }
2252 exception_handler = savehandler;
2253 RESTORE_INT(saveint);
2254 return err;
2255}
2256#endif
2257
2258/*
2259 * Unset the specified variable.
2260 */
2261static int
2262unsetvar(const char *s)
2263{
2264 struct var **vpp;
2265 struct var *vp;
2266 int retval;
2267
2268 vpp = findvar(hashvar(s), s);
2269 vp = *vpp;
2270 retval = 2;
2271 if (vp) {
2272 int flags = vp->flags;
2273
2274 retval = 1;
2275 if (flags & VREADONLY)
2276 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002277#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002278 vp->flags &= ~VDYNAMIC;
2279#endif
2280 if (flags & VUNSET)
2281 goto ok;
2282 if ((flags & VSTRFIXED) == 0) {
2283 INT_OFF;
2284 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002285 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002286 *vpp = vp->next;
2287 free(vp);
2288 INT_ON;
2289 } else {
2290 setvar(s, 0, 0);
2291 vp->flags &= ~VEXPORT;
2292 }
2293 ok:
2294 retval = 0;
2295 }
2296 out:
2297 return retval;
2298}
2299
2300/*
2301 * Process a linked list of variable assignments.
2302 */
2303static void
2304listsetvar(struct strlist *list_set_var, int flags)
2305{
2306 struct strlist *lp = list_set_var;
2307
2308 if (!lp)
2309 return;
2310 INT_OFF;
2311 do {
2312 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002313 lp = lp->next;
2314 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002315 INT_ON;
2316}
2317
2318/*
2319 * Generate a list of variables satisfying the given conditions.
2320 */
2321static char **
2322listvars(int on, int off, char ***end)
2323{
2324 struct var **vpp;
2325 struct var *vp;
2326 char **ep;
2327 int mask;
2328
2329 STARTSTACKSTR(ep);
2330 vpp = vartab;
2331 mask = on | off;
2332 do {
2333 for (vp = *vpp; vp; vp = vp->next) {
2334 if ((vp->flags & mask) == on) {
2335 if (ep == stackstrend())
2336 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002337 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002338 }
2339 }
2340 } while (++vpp < vartab + VTABSIZE);
2341 if (ep == stackstrend())
2342 ep = growstackstr();
2343 if (end)
2344 *end = ep;
2345 *ep++ = NULL;
2346 return grabstackstr(ep);
2347}
2348
2349
2350/* ============ Path search helper
2351 *
2352 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002353 * of the path before the first call; path_advance will update
2354 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002355 * the possible path expansions in sequence. If an option (indicated by
2356 * a percent sign) appears in the path entry then the global variable
2357 * pathopt will be set to point to it; otherwise pathopt will be set to
2358 * NULL.
2359 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002360static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002361
2362static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002363path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002364{
2365 const char *p;
2366 char *q;
2367 const char *start;
2368 size_t len;
2369
2370 if (*path == NULL)
2371 return NULL;
2372 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002373 for (p = start; *p && *p != ':' && *p != '%'; p++)
2374 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002375 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2376 while (stackblocksize() < len)
2377 growstackblock();
2378 q = stackblock();
2379 if (p != start) {
2380 memcpy(q, start, p - start);
2381 q += p - start;
2382 *q++ = '/';
2383 }
2384 strcpy(q, name);
2385 pathopt = NULL;
2386 if (*p == '%') {
2387 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002388 while (*p && *p != ':')
2389 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002390 }
2391 if (*p == ':')
2392 *path = p + 1;
2393 else
2394 *path = NULL;
2395 return stalloc(len);
2396}
2397
2398
2399/* ============ Prompt */
2400
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002401static smallint doprompt; /* if set, prompt the user */
2402static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002403
2404#if ENABLE_FEATURE_EDITING
2405static line_input_t *line_input_state;
2406static const char *cmdedit_prompt;
2407static void
2408putprompt(const char *s)
2409{
2410 if (ENABLE_ASH_EXPAND_PRMT) {
2411 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002412 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002413 return;
2414 }
2415 cmdedit_prompt = s;
2416}
2417#else
2418static void
2419putprompt(const char *s)
2420{
2421 out2str(s);
2422}
2423#endif
2424
2425#if ENABLE_ASH_EXPAND_PRMT
2426/* expandstr() needs parsing machinery, so it is far away ahead... */
2427static const char *expandstr(const char *ps);
2428#else
2429#define expandstr(s) s
2430#endif
2431
2432static void
2433setprompt(int whichprompt)
2434{
2435 const char *prompt;
2436#if ENABLE_ASH_EXPAND_PRMT
2437 struct stackmark smark;
2438#endif
2439
2440 needprompt = 0;
2441
2442 switch (whichprompt) {
2443 case 1:
2444 prompt = ps1val();
2445 break;
2446 case 2:
2447 prompt = ps2val();
2448 break;
2449 default: /* 0 */
2450 prompt = nullstr;
2451 }
2452#if ENABLE_ASH_EXPAND_PRMT
2453 setstackmark(&smark);
2454 stalloc(stackblocksize());
2455#endif
2456 putprompt(expandstr(prompt));
2457#if ENABLE_ASH_EXPAND_PRMT
2458 popstackmark(&smark);
2459#endif
2460}
2461
2462
2463/* ============ The cd and pwd commands */
2464
2465#define CD_PHYSICAL 1
2466#define CD_PRINT 2
2467
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002468static int
2469cdopt(void)
2470{
2471 int flags = 0;
2472 int i, j;
2473
2474 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002475 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002476 if (i != j) {
2477 flags ^= CD_PHYSICAL;
2478 j = i;
2479 }
2480 }
2481
2482 return flags;
2483}
2484
2485/*
2486 * Update curdir (the name of the current directory) in response to a
2487 * cd command.
2488 */
2489static const char *
2490updatepwd(const char *dir)
2491{
2492 char *new;
2493 char *p;
2494 char *cdcomppath;
2495 const char *lim;
2496
2497 cdcomppath = ststrdup(dir);
2498 STARTSTACKSTR(new);
2499 if (*dir != '/') {
2500 if (curdir == nullstr)
2501 return 0;
2502 new = stack_putstr(curdir, new);
2503 }
2504 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002505 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002506 if (*dir != '/') {
2507 if (new[-1] != '/')
2508 USTPUTC('/', new);
2509 if (new > lim && *lim == '/')
2510 lim++;
2511 } else {
2512 USTPUTC('/', new);
2513 cdcomppath++;
2514 if (dir[1] == '/' && dir[2] != '/') {
2515 USTPUTC('/', new);
2516 cdcomppath++;
2517 lim++;
2518 }
2519 }
2520 p = strtok(cdcomppath, "/");
2521 while (p) {
2522 switch (*p) {
2523 case '.':
2524 if (p[1] == '.' && p[2] == '\0') {
2525 while (new > lim) {
2526 STUNPUTC(new);
2527 if (new[-1] == '/')
2528 break;
2529 }
2530 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002531 }
2532 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002533 break;
2534 /* fall through */
2535 default:
2536 new = stack_putstr(p, new);
2537 USTPUTC('/', new);
2538 }
2539 p = strtok(0, "/");
2540 }
2541 if (new > lim)
2542 STUNPUTC(new);
2543 *new = 0;
2544 return stackblock();
2545}
2546
2547/*
2548 * Find out what the current directory is. If we already know the current
2549 * directory, this routine returns immediately.
2550 */
2551static char *
2552getpwd(void)
2553{
Denis Vlasenko01631112007-12-16 17:20:38 +00002554 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002555 return dir ? dir : nullstr;
2556}
2557
2558static void
2559setpwd(const char *val, int setold)
2560{
2561 char *oldcur, *dir;
2562
2563 oldcur = dir = curdir;
2564
2565 if (setold) {
2566 setvar("OLDPWD", oldcur, VEXPORT);
2567 }
2568 INT_OFF;
2569 if (physdir != nullstr) {
2570 if (physdir != oldcur)
2571 free(physdir);
2572 physdir = nullstr;
2573 }
2574 if (oldcur == val || !val) {
2575 char *s = getpwd();
2576 physdir = s;
2577 if (!val)
2578 dir = s;
2579 } else
2580 dir = ckstrdup(val);
2581 if (oldcur != dir && oldcur != nullstr) {
2582 free(oldcur);
2583 }
2584 curdir = dir;
2585 INT_ON;
2586 setvar("PWD", dir, VEXPORT);
2587}
2588
2589static void hashcd(void);
2590
2591/*
2592 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2593 * know that the current directory has changed.
2594 */
2595static int
2596docd(const char *dest, int flags)
2597{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002598 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002599 int err;
2600
2601 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2602
2603 INT_OFF;
2604 if (!(flags & CD_PHYSICAL)) {
2605 dir = updatepwd(dest);
2606 if (dir)
2607 dest = dir;
2608 }
2609 err = chdir(dest);
2610 if (err)
2611 goto out;
2612 setpwd(dir, 1);
2613 hashcd();
2614 out:
2615 INT_ON;
2616 return err;
2617}
2618
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002619static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002620cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002621{
2622 const char *dest;
2623 const char *path;
2624 const char *p;
2625 char c;
2626 struct stat statb;
2627 int flags;
2628
2629 flags = cdopt();
2630 dest = *argptr;
2631 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002632 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002633 else if (LONE_DASH(dest)) {
2634 dest = bltinlookup("OLDPWD");
2635 flags |= CD_PRINT;
2636 }
2637 if (!dest)
2638 dest = nullstr;
2639 if (*dest == '/')
2640 goto step7;
2641 if (*dest == '.') {
2642 c = dest[1];
2643 dotdot:
2644 switch (c) {
2645 case '\0':
2646 case '/':
2647 goto step6;
2648 case '.':
2649 c = dest[2];
2650 if (c != '.')
2651 goto dotdot;
2652 }
2653 }
2654 if (!*dest)
2655 dest = ".";
2656 path = bltinlookup("CDPATH");
2657 if (!path) {
2658 step6:
2659 step7:
2660 p = dest;
2661 goto docd;
2662 }
2663 do {
2664 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002665 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002666 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2667 if (c && c != ':')
2668 flags |= CD_PRINT;
2669 docd:
2670 if (!docd(p, flags))
2671 goto out;
2672 break;
2673 }
2674 } while (path);
2675 ash_msg_and_raise_error("can't cd to %s", dest);
2676 /* NOTREACHED */
2677 out:
2678 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002679 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002680 return 0;
2681}
2682
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002683static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002684pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002685{
2686 int flags;
2687 const char *dir = curdir;
2688
2689 flags = cdopt();
2690 if (flags) {
2691 if (physdir == nullstr)
2692 setpwd(dir, 0);
2693 dir = physdir;
2694 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002695 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002696 return 0;
2697}
2698
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002699
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002700/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002701
Denis Vlasenko834dee72008-10-07 09:18:30 +00002702
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002703#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002704
Eric Andersenc470f442003-07-28 09:56:35 +00002705/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002706#define CWORD 0 /* character is nothing special */
2707#define CNL 1 /* newline character */
2708#define CBACK 2 /* a backslash character */
2709#define CSQUOTE 3 /* single quote */
2710#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002711#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002712#define CBQUOTE 6 /* backwards single quote */
2713#define CVAR 7 /* a dollar sign */
2714#define CENDVAR 8 /* a '}' character */
2715#define CLP 9 /* a left paren in arithmetic */
2716#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002717#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002718#define CCTL 12 /* like CWORD, except it must be escaped */
2719#define CSPCL 13 /* these terminate a word */
2720#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002721
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002722#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002723#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002724# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002725#endif
2726
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002727#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002728
Mike Frysinger98c52642009-04-02 10:02:37 +00002729#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002730# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002731#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002732# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002733#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002734static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002735#if ENABLE_ASH_ALIAS
2736 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2737#endif
2738 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2739 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2740 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2741 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2742 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2743 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2744 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2745 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2746 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2747 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2748 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002749#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002750 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2751 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2752 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2753#endif
2754#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002755};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002756/* Constants below must match table above */
2757enum {
2758#if ENABLE_ASH_ALIAS
2759 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2760#endif
2761 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2762 CNL_CNL_CNL_CNL , /* 2 */
2763 CWORD_CCTL_CCTL_CWORD , /* 3 */
2764 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2765 CVAR_CVAR_CWORD_CVAR , /* 5 */
2766 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2767 CSPCL_CWORD_CWORD_CLP , /* 7 */
2768 CSPCL_CWORD_CWORD_CRP , /* 8 */
2769 CBACK_CBACK_CCTL_CBACK , /* 9 */
2770 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2771 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2772 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2773 CWORD_CWORD_CWORD_CWORD , /* 13 */
2774 CCTL_CCTL_CCTL_CCTL , /* 14 */
2775};
Eric Andersen2870d962001-07-02 17:27:21 +00002776
Denys Vlasenkocd716832009-11-28 22:14:02 +01002777/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2778 * caller must ensure proper cast on it if c is *char_ptr!
2779 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002780/* Values for syntax param */
2781#define BASESYNTAX 0 /* not in quotes */
2782#define DQSYNTAX 1 /* in double quotes */
2783#define SQSYNTAX 2 /* in single quotes */
2784#define ARISYNTAX 3 /* in arithmetic */
2785#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002786
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002787#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002788
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002789static int
2790SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002791{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002792 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002793# if ENABLE_ASH_ALIAS
2794 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002795 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2796 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2797 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2798 11, 3 /* "}~" */
2799 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002800# else
2801 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002802 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2803 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2804 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2805 10, 2 /* "}~" */
2806 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002807# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002808 const char *s;
2809 int indx;
2810
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002811 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002812 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002813# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002814 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002815 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002816 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002817# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002818 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002819 /* Cast is purely for paranoia here,
2820 * just in case someone passed signed char to us */
2821 if ((unsigned char)c >= CTL_FIRST
2822 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002823 ) {
2824 return CCTL;
2825 }
2826 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002827 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002828 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002829 indx = syntax_index_table[s - spec_symbls];
2830 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002831 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002832}
2833
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002834#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002835
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002836static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002837 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002838 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2848 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2849 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2871 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2872 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2873 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2875 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2878 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2879 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2880 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2881 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2883 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2884 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2885 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2886 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2897 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2898 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2899 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2900 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2901 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2902 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2930 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2931 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2932 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2935 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2963 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2964 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2965 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2966 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2967 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2968 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2969 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2970 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2971 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2972 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2973 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2974 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2975 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2976 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2977 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2978 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2979 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2980 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2981 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2982 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2983 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2986 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 150 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 151 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 152 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 153 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 154 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 155 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 156 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 157 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 158 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 159 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 160 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003094 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003095# if ENABLE_ASH_ALIAS
3096 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3097# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003098};
3099
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003100# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003101
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003102#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003103
Eric Andersen2870d962001-07-02 17:27:21 +00003104
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003105/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003106
Denis Vlasenko131ae172007-02-18 13:00:19 +00003107#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003108
3109#define ALIASINUSE 1
3110#define ALIASDEAD 2
3111
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003112struct alias {
3113 struct alias *next;
3114 char *name;
3115 char *val;
3116 int flag;
3117};
3118
Denis Vlasenko01631112007-12-16 17:20:38 +00003119
3120static struct alias **atab; // [ATABSIZE];
3121#define INIT_G_alias() do { \
3122 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3123} while (0)
3124
Eric Andersen2870d962001-07-02 17:27:21 +00003125
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003126static struct alias **
3127__lookupalias(const char *name) {
3128 unsigned int hashval;
3129 struct alias **app;
3130 const char *p;
3131 unsigned int ch;
3132
3133 p = name;
3134
3135 ch = (unsigned char)*p;
3136 hashval = ch << 4;
3137 while (ch) {
3138 hashval += ch;
3139 ch = (unsigned char)*++p;
3140 }
3141 app = &atab[hashval % ATABSIZE];
3142
3143 for (; *app; app = &(*app)->next) {
3144 if (strcmp(name, (*app)->name) == 0) {
3145 break;
3146 }
3147 }
3148
3149 return app;
3150}
3151
3152static struct alias *
3153lookupalias(const char *name, int check)
3154{
3155 struct alias *ap = *__lookupalias(name);
3156
3157 if (check && ap && (ap->flag & ALIASINUSE))
3158 return NULL;
3159 return ap;
3160}
3161
3162static struct alias *
3163freealias(struct alias *ap)
3164{
3165 struct alias *next;
3166
3167 if (ap->flag & ALIASINUSE) {
3168 ap->flag |= ALIASDEAD;
3169 return ap;
3170 }
3171
3172 next = ap->next;
3173 free(ap->name);
3174 free(ap->val);
3175 free(ap);
3176 return next;
3177}
Eric Andersencb57d552001-06-28 07:25:16 +00003178
Eric Andersenc470f442003-07-28 09:56:35 +00003179static void
3180setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003181{
3182 struct alias *ap, **app;
3183
3184 app = __lookupalias(name);
3185 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003186 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003187 if (ap) {
3188 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003189 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003190 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003191 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003192 ap->flag &= ~ALIASDEAD;
3193 } else {
3194 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003195 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003196 ap->name = ckstrdup(name);
3197 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003198 /*ap->flag = 0; - ckzalloc did it */
3199 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003200 *app = ap;
3201 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003202 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003203}
3204
Eric Andersenc470f442003-07-28 09:56:35 +00003205static int
3206unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003207{
Eric Andersencb57d552001-06-28 07:25:16 +00003208 struct alias **app;
3209
3210 app = __lookupalias(name);
3211
3212 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003213 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003214 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003215 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003216 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003217 }
3218
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003219 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003220}
3221
Eric Andersenc470f442003-07-28 09:56:35 +00003222static void
3223rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003224{
Eric Andersencb57d552001-06-28 07:25:16 +00003225 struct alias *ap, **app;
3226 int i;
3227
Denis Vlasenkob012b102007-02-19 22:43:01 +00003228 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003229 for (i = 0; i < ATABSIZE; i++) {
3230 app = &atab[i];
3231 for (ap = *app; ap; ap = *app) {
3232 *app = freealias(*app);
3233 if (ap == *app) {
3234 app = &ap->next;
3235 }
3236 }
3237 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003238 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003239}
3240
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003241static void
3242printalias(const struct alias *ap)
3243{
3244 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3245}
3246
Eric Andersencb57d552001-06-28 07:25:16 +00003247/*
3248 * TODO - sort output
3249 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003250static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003251aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003252{
3253 char *n, *v;
3254 int ret = 0;
3255 struct alias *ap;
3256
Denis Vlasenko68404f12008-03-17 09:00:54 +00003257 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003258 int i;
3259
Denis Vlasenko68404f12008-03-17 09:00:54 +00003260 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003261 for (ap = atab[i]; ap; ap = ap->next) {
3262 printalias(ap);
3263 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003264 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003265 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003266 }
3267 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003268 v = strchr(n+1, '=');
3269 if (v == NULL) { /* n+1: funny ksh stuff */
3270 ap = *__lookupalias(n);
3271 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003272 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003273 ret = 1;
3274 } else
3275 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003276 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003277 *v++ = '\0';
3278 setalias(n, v);
3279 }
3280 }
3281
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003282 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003283}
3284
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003285static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003286unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003287{
3288 int i;
3289
3290 while ((i = nextopt("a")) != '\0') {
3291 if (i == 'a') {
3292 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003293 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003294 }
3295 }
3296 for (i = 0; *argptr; argptr++) {
3297 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003298 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003299 i = 1;
3300 }
3301 }
3302
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003303 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003304}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003305
Denis Vlasenko131ae172007-02-18 13:00:19 +00003306#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003307
Eric Andersenc470f442003-07-28 09:56:35 +00003308
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003309/* ============ jobs.c */
3310
3311/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003312#define FORK_FG 0
3313#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003314#define FORK_NOJOB 2
3315
3316/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003317#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3318#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3319#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321/*
3322 * A job structure contains information about a job. A job is either a
3323 * single process or a set of processes contained in a pipeline. In the
3324 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3325 * array of pids.
3326 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003328 pid_t ps_pid; /* process id */
3329 int ps_status; /* last process status from wait() */
3330 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331};
3332
3333struct job {
3334 struct procstat ps0; /* status of process */
3335 struct procstat *ps; /* status or processes when more than one */
3336#if JOBS
3337 int stopstatus; /* status of a stopped job */
3338#endif
3339 uint32_t
3340 nprocs: 16, /* number of processes */
3341 state: 8,
3342#define JOBRUNNING 0 /* at least one proc running */
3343#define JOBSTOPPED 1 /* all procs are stopped */
3344#define JOBDONE 2 /* all procs are completed */
3345#if JOBS
3346 sigint: 1, /* job was killed by SIGINT */
3347 jobctl: 1, /* job running under job control */
3348#endif
3349 waited: 1, /* true if this entry has been waited for */
3350 used: 1, /* true if this entry is in used */
3351 changed: 1; /* true if status has changed */
3352 struct job *prev_job; /* previous job */
3353};
3354
Denis Vlasenko68404f12008-03-17 09:00:54 +00003355static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356static int forkshell(struct job *, union node *, int);
3357static int waitforjob(struct job *);
3358
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003359#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003360enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003361#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003363static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003365#endif
3366
3367/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003368 * Ignore a signal.
3369 */
3370static void
3371ignoresig(int signo)
3372{
3373 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3374 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3375 /* No, need to do it */
3376 signal(signo, SIG_IGN);
3377 }
3378 sigmode[signo - 1] = S_HARD_IGN;
3379}
3380
3381/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003382 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003383 */
3384static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003385signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003386{
3387 gotsig[signo - 1] = 1;
3388
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003389 if (signo == SIGINT && !trap[SIGINT]) {
3390 if (!suppress_int) {
3391 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003392 raise_interrupt(); /* does not return */
3393 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003394 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003395 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003396 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003397 }
3398}
3399
3400/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003401 * Set the signal handler for the specified signal. The routine figures
3402 * out what it should be set to.
3403 */
3404static void
3405setsignal(int signo)
3406{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003407 char *t;
3408 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003409 struct sigaction act;
3410
3411 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003412 new_act = S_DFL;
3413 if (t != NULL) { /* trap for this sig is set */
3414 new_act = S_CATCH;
3415 if (t[0] == '\0') /* trap is "": ignore this sig */
3416 new_act = S_IGN;
3417 }
3418
3419 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003420 switch (signo) {
3421 case SIGINT:
3422 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003423 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003424 break;
3425 case SIGQUIT:
3426#if DEBUG
3427 if (debug)
3428 break;
3429#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003430 /* man bash:
3431 * "In all cases, bash ignores SIGQUIT. Non-builtin
3432 * commands run by bash have signal handlers
3433 * set to the values inherited by the shell
3434 * from its parent". */
3435 new_act = S_IGN;
3436 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003437 case SIGTERM:
3438 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003439 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003440 break;
3441#if JOBS
3442 case SIGTSTP:
3443 case SIGTTOU:
3444 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003445 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003446 break;
3447#endif
3448 }
3449 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003450//TODO: if !rootshell, we reset SIGQUIT to DFL,
3451//whereas we have to restore it to what shell got on entry
3452//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003453
3454 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003455 cur_act = *t;
3456 if (cur_act == 0) {
3457 /* current setting is not yet known */
3458 if (sigaction(signo, NULL, &act)) {
3459 /* pretend it worked; maybe we should give a warning,
3460 * but other shells don't. We don't alter sigmode,
3461 * so we retry every time.
3462 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003463 return;
3464 }
3465 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003466 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003467 if (mflag
3468 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3469 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003470 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003471 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003472 }
3473 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003474 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003475 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003476
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003477 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003478 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003479 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003480 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003481 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3482 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003483 break;
3484 case S_IGN:
3485 act.sa_handler = SIG_IGN;
3486 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003487 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003488 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003489
3490 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003491}
3492
3493/* mode flags for set_curjob */
3494#define CUR_DELETE 2
3495#define CUR_RUNNING 1
3496#define CUR_STOPPED 0
3497
3498/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003499#define DOWAIT_NONBLOCK WNOHANG
3500#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003501
3502#if JOBS
3503/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003504static int initialpgrp; //references:2
3505static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003506#endif
3507/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003508static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003509/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003510static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003511/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003512static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003513/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003514static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003515
3516static void
3517set_curjob(struct job *jp, unsigned mode)
3518{
3519 struct job *jp1;
3520 struct job **jpp, **curp;
3521
3522 /* first remove from list */
3523 jpp = curp = &curjob;
3524 do {
3525 jp1 = *jpp;
3526 if (jp1 == jp)
3527 break;
3528 jpp = &jp1->prev_job;
3529 } while (1);
3530 *jpp = jp1->prev_job;
3531
3532 /* Then re-insert in correct position */
3533 jpp = curp;
3534 switch (mode) {
3535 default:
3536#if DEBUG
3537 abort();
3538#endif
3539 case CUR_DELETE:
3540 /* job being deleted */
3541 break;
3542 case CUR_RUNNING:
3543 /* newly created job or backgrounded job,
3544 put after all stopped jobs. */
3545 do {
3546 jp1 = *jpp;
3547#if JOBS
3548 if (!jp1 || jp1->state != JOBSTOPPED)
3549#endif
3550 break;
3551 jpp = &jp1->prev_job;
3552 } while (1);
3553 /* FALLTHROUGH */
3554#if JOBS
3555 case CUR_STOPPED:
3556#endif
3557 /* newly stopped job - becomes curjob */
3558 jp->prev_job = *jpp;
3559 *jpp = jp;
3560 break;
3561 }
3562}
3563
3564#if JOBS || DEBUG
3565static int
3566jobno(const struct job *jp)
3567{
3568 return jp - jobtab + 1;
3569}
3570#endif
3571
3572/*
3573 * Convert a job name to a job structure.
3574 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003575#if !JOBS
3576#define getjob(name, getctl) getjob(name)
3577#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003578static struct job *
3579getjob(const char *name, int getctl)
3580{
3581 struct job *jp;
3582 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003583 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003584 unsigned num;
3585 int c;
3586 const char *p;
3587 char *(*match)(const char *, const char *);
3588
3589 jp = curjob;
3590 p = name;
3591 if (!p)
3592 goto currentjob;
3593
3594 if (*p != '%')
3595 goto err;
3596
3597 c = *++p;
3598 if (!c)
3599 goto currentjob;
3600
3601 if (!p[1]) {
3602 if (c == '+' || c == '%') {
3603 currentjob:
3604 err_msg = "No current job";
3605 goto check;
3606 }
3607 if (c == '-') {
3608 if (jp)
3609 jp = jp->prev_job;
3610 err_msg = "No previous job";
3611 check:
3612 if (!jp)
3613 goto err;
3614 goto gotit;
3615 }
3616 }
3617
3618 if (is_number(p)) {
3619 num = atoi(p);
3620 if (num < njobs) {
3621 jp = jobtab + num - 1;
3622 if (jp->used)
3623 goto gotit;
3624 goto err;
3625 }
3626 }
3627
3628 match = prefix;
3629 if (*p == '?') {
3630 match = strstr;
3631 p++;
3632 }
3633
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003634 found = NULL;
3635 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003636 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003637 if (found)
3638 goto err;
3639 found = jp;
3640 err_msg = "%s: ambiguous";
3641 }
3642 jp = jp->prev_job;
3643 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003644 if (!found)
3645 goto err;
3646 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003647
3648 gotit:
3649#if JOBS
3650 err_msg = "job %s not created under job control";
3651 if (getctl && jp->jobctl == 0)
3652 goto err;
3653#endif
3654 return jp;
3655 err:
3656 ash_msg_and_raise_error(err_msg, name);
3657}
3658
3659/*
3660 * Mark a job structure as unused.
3661 */
3662static void
3663freejob(struct job *jp)
3664{
3665 struct procstat *ps;
3666 int i;
3667
3668 INT_OFF;
3669 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003670 if (ps->ps_cmd != nullstr)
3671 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003672 }
3673 if (jp->ps != &jp->ps0)
3674 free(jp->ps);
3675 jp->used = 0;
3676 set_curjob(jp, CUR_DELETE);
3677 INT_ON;
3678}
3679
3680#if JOBS
3681static void
3682xtcsetpgrp(int fd, pid_t pgrp)
3683{
3684 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003685 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686}
3687
3688/*
3689 * Turn job control on and off.
3690 *
3691 * Note: This code assumes that the third arg to ioctl is a character
3692 * pointer, which is true on Berkeley systems but not System V. Since
3693 * System V doesn't have job control yet, this isn't a problem now.
3694 *
3695 * Called with interrupts off.
3696 */
3697static void
3698setjobctl(int on)
3699{
3700 int fd;
3701 int pgrp;
3702
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003703 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003704 return;
3705 if (on) {
3706 int ofd;
3707 ofd = fd = open(_PATH_TTY, O_RDWR);
3708 if (fd < 0) {
3709 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3710 * That sometimes helps to acquire controlling tty.
3711 * Obviously, a workaround for bugs when someone
3712 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003713 fd = 2;
3714 while (!isatty(fd))
3715 if (--fd < 0)
3716 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003717 }
3718 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003719 if (ofd >= 0)
3720 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003721 if (fd < 0)
3722 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003723 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003724 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003725 do { /* while we are in the background */
3726 pgrp = tcgetpgrp(fd);
3727 if (pgrp < 0) {
3728 out:
3729 ash_msg("can't access tty; job control turned off");
3730 mflag = on = 0;
3731 goto close;
3732 }
3733 if (pgrp == getpgrp())
3734 break;
3735 killpg(0, SIGTTIN);
3736 } while (1);
3737 initialpgrp = pgrp;
3738
3739 setsignal(SIGTSTP);
3740 setsignal(SIGTTOU);
3741 setsignal(SIGTTIN);
3742 pgrp = rootpid;
3743 setpgid(0, pgrp);
3744 xtcsetpgrp(fd, pgrp);
3745 } else {
3746 /* turning job control off */
3747 fd = ttyfd;
3748 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003749 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003750 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003751 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003752 setpgid(0, pgrp);
3753 setsignal(SIGTSTP);
3754 setsignal(SIGTTOU);
3755 setsignal(SIGTTIN);
3756 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003757 if (fd >= 0)
3758 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003759 fd = -1;
3760 }
3761 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003762 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003763}
3764
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003765static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003766killcmd(int argc, char **argv)
3767{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003768 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003769 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003770 do {
3771 if (argv[i][0] == '%') {
3772 struct job *jp = getjob(argv[i], 0);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003773 unsigned pid = jp->ps[0].ps_pid;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003774 /* Enough space for ' -NNN<nul>' */
3775 argv[i] = alloca(sizeof(int)*3 + 3);
3776 /* kill_main has matching code to expect
3777 * leading space. Needed to not confuse
3778 * negative pids with "kill -SIGNAL_NO" syntax */
3779 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003781 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003782 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003783 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784}
3785
3786static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003787showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003789 struct procstat *ps;
3790 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791
Denys Vlasenko285ad152009-12-04 23:02:27 +01003792 psend = jp->ps + jp->nprocs;
3793 for (ps = jp->ps + 1; ps < psend; ps++)
3794 printf(" | %s", ps->ps_cmd);
3795 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796 flush_stdout_stderr();
3797}
3798
3799
3800static int
3801restartjob(struct job *jp, int mode)
3802{
3803 struct procstat *ps;
3804 int i;
3805 int status;
3806 pid_t pgid;
3807
3808 INT_OFF;
3809 if (jp->state == JOBDONE)
3810 goto out;
3811 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003812 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003813 if (mode == FORK_FG)
3814 xtcsetpgrp(ttyfd, pgid);
3815 killpg(pgid, SIGCONT);
3816 ps = jp->ps;
3817 i = jp->nprocs;
3818 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003819 if (WIFSTOPPED(ps->ps_status)) {
3820 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003821 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003822 ps++;
3823 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003824 out:
3825 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3826 INT_ON;
3827 return status;
3828}
3829
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003830static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003831fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003832{
3833 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003834 int mode;
3835 int retval;
3836
3837 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3838 nextopt(nullstr);
3839 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840 do {
3841 jp = getjob(*argv, 1);
3842 if (mode == FORK_BG) {
3843 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003844 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003845 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003846 out1str(jp->ps[0].ps_cmd);
3847 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003848 retval = restartjob(jp, mode);
3849 } while (*argv && *++argv);
3850 return retval;
3851}
3852#endif
3853
3854static int
3855sprint_status(char *s, int status, int sigonly)
3856{
3857 int col;
3858 int st;
3859
3860 col = 0;
3861 if (!WIFEXITED(status)) {
3862#if JOBS
3863 if (WIFSTOPPED(status))
3864 st = WSTOPSIG(status);
3865 else
3866#endif
3867 st = WTERMSIG(status);
3868 if (sigonly) {
3869 if (st == SIGINT || st == SIGPIPE)
3870 goto out;
3871#if JOBS
3872 if (WIFSTOPPED(status))
3873 goto out;
3874#endif
3875 }
3876 st &= 0x7f;
3877 col = fmtstr(s, 32, strsignal(st));
3878 if (WCOREDUMP(status)) {
3879 col += fmtstr(s + col, 16, " (core dumped)");
3880 }
3881 } else if (!sigonly) {
3882 st = WEXITSTATUS(status);
3883 if (st)
3884 col = fmtstr(s, 16, "Done(%d)", st);
3885 else
3886 col = fmtstr(s, 16, "Done");
3887 }
3888 out:
3889 return col;
3890}
3891
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003892static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003893dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003894{
3895 int pid;
3896 int status;
3897 struct job *jp;
3898 struct job *thisjob;
3899 int state;
3900
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003901 TRACE(("dowait(0x%x) called\n", wait_flags));
3902
3903 /* Do a wait system call. If job control is compiled in, we accept
3904 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3905 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003906 if (doing_jobctl)
3907 wait_flags |= WUNTRACED;
3908 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003909 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3910 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003911 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003912 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003913
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003914 INT_OFF;
3915 thisjob = NULL;
3916 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003917 struct procstat *ps;
3918 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003919 if (jp->state == JOBDONE)
3920 continue;
3921 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003922 ps = jp->ps;
3923 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003924 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003925 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003926 TRACE(("Job %d: changing status of proc %d "
3927 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003928 jobno(jp), pid, ps->ps_status, status));
3929 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003930 thisjob = jp;
3931 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003932 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003933 state = JOBRUNNING;
3934#if JOBS
3935 if (state == JOBRUNNING)
3936 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003937 if (WIFSTOPPED(ps->ps_status)) {
3938 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003939 state = JOBSTOPPED;
3940 }
3941#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003942 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003943 if (thisjob)
3944 goto gotjob;
3945 }
3946#if JOBS
3947 if (!WIFSTOPPED(status))
3948#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 jobless--;
3950 goto out;
3951
3952 gotjob:
3953 if (state != JOBRUNNING) {
3954 thisjob->changed = 1;
3955
3956 if (thisjob->state != state) {
3957 TRACE(("Job %d: changing state from %d to %d\n",
3958 jobno(thisjob), thisjob->state, state));
3959 thisjob->state = state;
3960#if JOBS
3961 if (state == JOBSTOPPED) {
3962 set_curjob(thisjob, CUR_STOPPED);
3963 }
3964#endif
3965 }
3966 }
3967
3968 out:
3969 INT_ON;
3970
3971 if (thisjob && thisjob == job) {
3972 char s[48 + 1];
3973 int len;
3974
3975 len = sprint_status(s, status, 1);
3976 if (len) {
3977 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003978 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003979 out2str(s);
3980 }
3981 }
3982 return pid;
3983}
3984
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003985static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003986blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003987{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003988 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003989 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003990 raise_exception(EXSIG);
3991 return pid;
3992}
3993
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003994#if JOBS
3995static void
3996showjob(FILE *out, struct job *jp, int mode)
3997{
3998 struct procstat *ps;
3999 struct procstat *psend;
4000 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004001 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004002 char s[80];
4003
4004 ps = jp->ps;
4005
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004006 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004007 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004008 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004009 return;
4010 }
4011
4012 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004013 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004014
4015 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004016 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004017 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004018 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004019
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004020 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004021 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004022
4023 psend = ps + jp->nprocs;
4024
4025 if (jp->state == JOBRUNNING) {
4026 strcpy(s + col, "Running");
4027 col += sizeof("Running") - 1;
4028 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004029 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004030 if (jp->state == JOBSTOPPED)
4031 status = jp->stopstatus;
4032 col += sprint_status(s + col, status, 0);
4033 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004034 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004035
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004036 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4037 * or prints several "PID | <cmdN>" lines,
4038 * depending on SHOW_PIDS bit.
4039 * We do not print status of individual processes
4040 * between PID and <cmdN>. bash does it, but not very well:
4041 * first line shows overall job status, not process status,
4042 * making it impossible to know 1st process status.
4043 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004044 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004045 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004046 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004047 s[0] = '\0';
4048 col = 33;
4049 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004050 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004052 fprintf(out, "%s%*c%s%s",
4053 s,
4054 33 - col >= 0 ? 33 - col : 0, ' ',
4055 ps == jp->ps ? "" : "| ",
4056 ps->ps_cmd
4057 );
4058 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004059 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004060
4061 jp->changed = 0;
4062
4063 if (jp->state == JOBDONE) {
4064 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4065 freejob(jp);
4066 }
4067}
4068
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004069/*
4070 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4071 * statuses have changed since the last call to showjobs.
4072 */
4073static void
4074showjobs(FILE *out, int mode)
4075{
4076 struct job *jp;
4077
Denys Vlasenko883cea42009-07-11 15:31:59 +02004078 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004079
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004080 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004081 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082 continue;
4083
4084 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004085 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004086 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004087 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 }
4089}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004090
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004091static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004092jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004093{
4094 int mode, m;
4095
4096 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004097 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004098 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004099 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004100 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004101 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004102 }
4103
4104 argv = argptr;
4105 if (*argv) {
4106 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004107 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004108 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004109 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004110 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004111 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004112
4113 return 0;
4114}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004115#endif /* JOBS */
4116
Michael Abbott359da5e2009-12-04 23:03:29 +01004117/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004118static int
4119getstatus(struct job *job)
4120{
4121 int status;
4122 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004123 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004124
Michael Abbott359da5e2009-12-04 23:03:29 +01004125 /* Fetch last member's status */
4126 ps = job->ps + job->nprocs - 1;
4127 status = ps->ps_status;
4128 if (pipefail) {
4129 /* "set -o pipefail" mode: use last _nonzero_ status */
4130 while (status == 0 && --ps >= job->ps)
4131 status = ps->ps_status;
4132 }
4133
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134 retval = WEXITSTATUS(status);
4135 if (!WIFEXITED(status)) {
4136#if JOBS
4137 retval = WSTOPSIG(status);
4138 if (!WIFSTOPPED(status))
4139#endif
4140 {
4141 /* XXX: limits number of signals */
4142 retval = WTERMSIG(status);
4143#if JOBS
4144 if (retval == SIGINT)
4145 job->sigint = 1;
4146#endif
4147 }
4148 retval += 128;
4149 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004150 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004151 jobno(job), job->nprocs, status, retval));
4152 return retval;
4153}
4154
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004155static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004156waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004157{
4158 struct job *job;
4159 int retval;
4160 struct job *jp;
4161
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004162 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004163 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004164
4165 nextopt(nullstr);
4166 retval = 0;
4167
4168 argv = argptr;
4169 if (!*argv) {
4170 /* wait for all jobs */
4171 for (;;) {
4172 jp = curjob;
4173 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004174 if (!jp) /* no running procs */
4175 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176 if (jp->state == JOBRUNNING)
4177 break;
4178 jp->waited = 1;
4179 jp = jp->prev_job;
4180 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004181 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004182 /* man bash:
4183 * "When bash is waiting for an asynchronous command via
4184 * the wait builtin, the reception of a signal for which a trap
4185 * has been set will cause the wait builtin to return immediately
4186 * with an exit status greater than 128, immediately after which
4187 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004188 *
4189 * blocking_wait_with_raise_on_sig raises signal handlers
4190 * if it gets no pid (pid < 0). However,
4191 * if child sends us a signal *and immediately exits*,
4192 * blocking_wait_with_raise_on_sig gets pid > 0
4193 * and does not handle pending_sig. Check this case: */
4194 if (pending_sig)
4195 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004196 }
4197 }
4198
4199 retval = 127;
4200 do {
4201 if (**argv != '%') {
4202 pid_t pid = number(*argv);
4203 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004204 while (1) {
4205 if (!job)
4206 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004207 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004208 break;
4209 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004210 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004211 } else
4212 job = getjob(*argv, 0);
4213 /* loop until process terminated or stopped */
4214 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004215 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004216 job->waited = 1;
4217 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004218 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004219 } while (*++argv);
4220
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004221 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004222 return retval;
4223}
4224
4225static struct job *
4226growjobtab(void)
4227{
4228 size_t len;
4229 ptrdiff_t offset;
4230 struct job *jp, *jq;
4231
4232 len = njobs * sizeof(*jp);
4233 jq = jobtab;
4234 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4235
4236 offset = (char *)jp - (char *)jq;
4237 if (offset) {
4238 /* Relocate pointers */
4239 size_t l = len;
4240
4241 jq = (struct job *)((char *)jq + l);
4242 while (l) {
4243 l -= sizeof(*jp);
4244 jq--;
4245#define joff(p) ((struct job *)((char *)(p) + l))
4246#define jmove(p) (p) = (void *)((char *)(p) + offset)
4247 if (joff(jp)->ps == &jq->ps0)
4248 jmove(joff(jp)->ps);
4249 if (joff(jp)->prev_job)
4250 jmove(joff(jp)->prev_job);
4251 }
4252 if (curjob)
4253 jmove(curjob);
4254#undef joff
4255#undef jmove
4256 }
4257
4258 njobs += 4;
4259 jobtab = jp;
4260 jp = (struct job *)((char *)jp + len);
4261 jq = jp + 3;
4262 do {
4263 jq->used = 0;
4264 } while (--jq >= jp);
4265 return jp;
4266}
4267
4268/*
4269 * Return a new job structure.
4270 * Called with interrupts off.
4271 */
4272static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004273makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004274{
4275 int i;
4276 struct job *jp;
4277
4278 for (i = njobs, jp = jobtab; ; jp++) {
4279 if (--i < 0) {
4280 jp = growjobtab();
4281 break;
4282 }
4283 if (jp->used == 0)
4284 break;
4285 if (jp->state != JOBDONE || !jp->waited)
4286 continue;
4287#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004288 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004289 continue;
4290#endif
4291 freejob(jp);
4292 break;
4293 }
4294 memset(jp, 0, sizeof(*jp));
4295#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004296 /* jp->jobctl is a bitfield.
4297 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004298 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004299 jp->jobctl = 1;
4300#endif
4301 jp->prev_job = curjob;
4302 curjob = jp;
4303 jp->used = 1;
4304 jp->ps = &jp->ps0;
4305 if (nprocs > 1) {
4306 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4307 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004308 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004309 jobno(jp)));
4310 return jp;
4311}
4312
4313#if JOBS
4314/*
4315 * Return a string identifying a command (to be printed by the
4316 * jobs command).
4317 */
4318static char *cmdnextc;
4319
4320static void
4321cmdputs(const char *s)
4322{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004323 static const char vstype[VSTYPE + 1][3] = {
4324 "", "}", "-", "+", "?", "=",
4325 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004326 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004327 };
4328
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004329 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004330 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004331 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004332 unsigned char c;
4333 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004334 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004335
Denys Vlasenko46a14772009-12-10 21:27:13 +01004336 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004337 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4338 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004339 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004340 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004341 switch (c) {
4342 case CTLESC:
4343 c = *p++;
4344 break;
4345 case CTLVAR:
4346 subtype = *p++;
4347 if ((subtype & VSTYPE) == VSLENGTH)
4348 str = "${#";
4349 else
4350 str = "${";
4351 if (!(subtype & VSQUOTE) == !(quoted & 1))
4352 goto dostr;
4353 quoted ^= 1;
4354 c = '"';
4355 break;
4356 case CTLENDVAR:
4357 str = "\"}" + !(quoted & 1);
4358 quoted >>= 1;
4359 subtype = 0;
4360 goto dostr;
4361 case CTLBACKQ:
4362 str = "$(...)";
4363 goto dostr;
4364 case CTLBACKQ+CTLQUOTE:
4365 str = "\"$(...)\"";
4366 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004367#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004368 case CTLARI:
4369 str = "$((";
4370 goto dostr;
4371 case CTLENDARI:
4372 str = "))";
4373 goto dostr;
4374#endif
4375 case CTLQUOTEMARK:
4376 quoted ^= 1;
4377 c = '"';
4378 break;
4379 case '=':
4380 if (subtype == 0)
4381 break;
4382 if ((subtype & VSTYPE) != VSNORMAL)
4383 quoted <<= 1;
4384 str = vstype[subtype & VSTYPE];
4385 if (subtype & VSNUL)
4386 c = ':';
4387 else
4388 goto checkstr;
4389 break;
4390 case '\'':
4391 case '\\':
4392 case '"':
4393 case '$':
4394 /* These can only happen inside quotes */
4395 cc[0] = c;
4396 str = cc;
4397 c = '\\';
4398 break;
4399 default:
4400 break;
4401 }
4402 USTPUTC(c, nextc);
4403 checkstr:
4404 if (!str)
4405 continue;
4406 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004407 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004408 USTPUTC(c, nextc);
4409 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004410 } /* while *p++ not NUL */
4411
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004412 if (quoted & 1) {
4413 USTPUTC('"', nextc);
4414 }
4415 *nextc = 0;
4416 cmdnextc = nextc;
4417}
4418
4419/* cmdtxt() and cmdlist() call each other */
4420static void cmdtxt(union node *n);
4421
4422static void
4423cmdlist(union node *np, int sep)
4424{
4425 for (; np; np = np->narg.next) {
4426 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004427 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004428 cmdtxt(np);
4429 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004430 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004431 }
4432}
4433
4434static void
4435cmdtxt(union node *n)
4436{
4437 union node *np;
4438 struct nodelist *lp;
4439 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004440
4441 if (!n)
4442 return;
4443 switch (n->type) {
4444 default:
4445#if DEBUG
4446 abort();
4447#endif
4448 case NPIPE:
4449 lp = n->npipe.cmdlist;
4450 for (;;) {
4451 cmdtxt(lp->n);
4452 lp = lp->next;
4453 if (!lp)
4454 break;
4455 cmdputs(" | ");
4456 }
4457 break;
4458 case NSEMI:
4459 p = "; ";
4460 goto binop;
4461 case NAND:
4462 p = " && ";
4463 goto binop;
4464 case NOR:
4465 p = " || ";
4466 binop:
4467 cmdtxt(n->nbinary.ch1);
4468 cmdputs(p);
4469 n = n->nbinary.ch2;
4470 goto donode;
4471 case NREDIR:
4472 case NBACKGND:
4473 n = n->nredir.n;
4474 goto donode;
4475 case NNOT:
4476 cmdputs("!");
4477 n = n->nnot.com;
4478 donode:
4479 cmdtxt(n);
4480 break;
4481 case NIF:
4482 cmdputs("if ");
4483 cmdtxt(n->nif.test);
4484 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004486 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004487 cmdputs("; else ");
4488 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004489 } else {
4490 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004491 }
4492 p = "; fi";
4493 goto dotail;
4494 case NSUBSHELL:
4495 cmdputs("(");
4496 n = n->nredir.n;
4497 p = ")";
4498 goto dotail;
4499 case NWHILE:
4500 p = "while ";
4501 goto until;
4502 case NUNTIL:
4503 p = "until ";
4504 until:
4505 cmdputs(p);
4506 cmdtxt(n->nbinary.ch1);
4507 n = n->nbinary.ch2;
4508 p = "; done";
4509 dodo:
4510 cmdputs("; do ");
4511 dotail:
4512 cmdtxt(n);
4513 goto dotail2;
4514 case NFOR:
4515 cmdputs("for ");
4516 cmdputs(n->nfor.var);
4517 cmdputs(" in ");
4518 cmdlist(n->nfor.args, 1);
4519 n = n->nfor.body;
4520 p = "; done";
4521 goto dodo;
4522 case NDEFUN:
4523 cmdputs(n->narg.text);
4524 p = "() { ... }";
4525 goto dotail2;
4526 case NCMD:
4527 cmdlist(n->ncmd.args, 1);
4528 cmdlist(n->ncmd.redirect, 0);
4529 break;
4530 case NARG:
4531 p = n->narg.text;
4532 dotail2:
4533 cmdputs(p);
4534 break;
4535 case NHERE:
4536 case NXHERE:
4537 p = "<<...";
4538 goto dotail2;
4539 case NCASE:
4540 cmdputs("case ");
4541 cmdputs(n->ncase.expr->narg.text);
4542 cmdputs(" in ");
4543 for (np = n->ncase.cases; np; np = np->nclist.next) {
4544 cmdtxt(np->nclist.pattern);
4545 cmdputs(") ");
4546 cmdtxt(np->nclist.body);
4547 cmdputs(";; ");
4548 }
4549 p = "esac";
4550 goto dotail2;
4551 case NTO:
4552 p = ">";
4553 goto redir;
4554 case NCLOBBER:
4555 p = ">|";
4556 goto redir;
4557 case NAPPEND:
4558 p = ">>";
4559 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004560#if ENABLE_ASH_BASH_COMPAT
4561 case NTO2:
4562#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004563 case NTOFD:
4564 p = ">&";
4565 goto redir;
4566 case NFROM:
4567 p = "<";
4568 goto redir;
4569 case NFROMFD:
4570 p = "<&";
4571 goto redir;
4572 case NFROMTO:
4573 p = "<>";
4574 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004575 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004576 cmdputs(p);
4577 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004578 cmdputs(utoa(n->ndup.dupfd));
4579 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004580 }
4581 n = n->nfile.fname;
4582 goto donode;
4583 }
4584}
4585
4586static char *
4587commandtext(union node *n)
4588{
4589 char *name;
4590
4591 STARTSTACKSTR(cmdnextc);
4592 cmdtxt(n);
4593 name = stackblock();
4594 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4595 name, cmdnextc, cmdnextc));
4596 return ckstrdup(name);
4597}
4598#endif /* JOBS */
4599
4600/*
4601 * Fork off a subshell. If we are doing job control, give the subshell its
4602 * own process group. Jp is a job structure that the job is to be added to.
4603 * N is the command that will be evaluated by the child. Both jp and n may
4604 * be NULL. The mode parameter can be one of the following:
4605 * FORK_FG - Fork off a foreground process.
4606 * FORK_BG - Fork off a background process.
4607 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4608 * process group even if job control is on.
4609 *
4610 * When job control is turned off, background processes have their standard
4611 * input redirected to /dev/null (except for the second and later processes
4612 * in a pipeline).
4613 *
4614 * Called with interrupts off.
4615 */
4616/*
4617 * Clear traps on a fork.
4618 */
4619static void
4620clear_traps(void)
4621{
4622 char **tp;
4623
4624 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004625 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004626 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004627 if (trap_ptr == trap)
4628 free(*tp);
4629 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004630 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004631 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004632 setsignal(tp - trap);
4633 INT_ON;
4634 }
4635 }
4636}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004637
4638/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004639static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004640
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004641/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004642static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004643forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004644{
4645 int oldlvl;
4646
4647 TRACE(("Child shell %d\n", getpid()));
4648 oldlvl = shlvl;
4649 shlvl++;
4650
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004651 /* man bash: "Non-builtin commands run by bash have signal handlers
4652 * set to the values inherited by the shell from its parent".
4653 * Do we do it correctly? */
4654
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004655 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004656
4657 if (mode == FORK_NOJOB /* is it `xxx` ? */
4658 && n && n->type == NCMD /* is it single cmd? */
4659 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004660 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004661 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4662 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4663 ) {
4664 TRACE(("Trap hack\n"));
4665 /* Awful hack for `trap` or $(trap).
4666 *
4667 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4668 * contains an example where "trap" is executed in a subshell:
4669 *
4670 * save_traps=$(trap)
4671 * ...
4672 * eval "$save_traps"
4673 *
4674 * Standard does not say that "trap" in subshell shall print
4675 * parent shell's traps. It only says that its output
4676 * must have suitable form, but then, in the above example
4677 * (which is not supposed to be normative), it implies that.
4678 *
4679 * bash (and probably other shell) does implement it
4680 * (traps are reset to defaults, but "trap" still shows them),
4681 * but as a result, "trap" logic is hopelessly messed up:
4682 *
4683 * # trap
4684 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4685 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4686 * # true | trap <--- trap is in subshell - no output (ditto)
4687 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4688 * trap -- 'echo Ho' SIGWINCH
4689 * # echo `(trap)` <--- in subshell in subshell - output
4690 * trap -- 'echo Ho' SIGWINCH
4691 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4692 * trap -- 'echo Ho' SIGWINCH
4693 *
4694 * The rules when to forget and when to not forget traps
4695 * get really complex and nonsensical.
4696 *
4697 * Our solution: ONLY bare $(trap) or `trap` is special.
4698 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004699 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004700 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004701 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004702 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004703 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004704#if JOBS
4705 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004706 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004707 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4708 pid_t pgrp;
4709
4710 if (jp->nprocs == 0)
4711 pgrp = getpid();
4712 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004713 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004714 /* this can fail because we are doing it in the parent also */
4715 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004716 if (mode == FORK_FG)
4717 xtcsetpgrp(ttyfd, pgrp);
4718 setsignal(SIGTSTP);
4719 setsignal(SIGTTOU);
4720 } else
4721#endif
4722 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004723 /* man bash: "When job control is not in effect,
4724 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004725 ignoresig(SIGINT);
4726 ignoresig(SIGQUIT);
4727 if (jp->nprocs == 0) {
4728 close(0);
4729 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004730 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004731 }
4732 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004733 if (!oldlvl) {
4734 if (iflag) { /* why if iflag only? */
4735 setsignal(SIGINT);
4736 setsignal(SIGTERM);
4737 }
4738 /* man bash:
4739 * "In all cases, bash ignores SIGQUIT. Non-builtin
4740 * commands run by bash have signal handlers
4741 * set to the values inherited by the shell
4742 * from its parent".
4743 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004744 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004745 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004746#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004747 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004748 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004749 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004750 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004751 /* "jobs": we do not want to clear job list for it,
4752 * instead we remove only _its_ own_ job from job list.
4753 * This makes "jobs .... | cat" more useful.
4754 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004755 freejob(curjob);
4756 return;
4757 }
4758#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004759 for (jp = curjob; jp; jp = jp->prev_job)
4760 freejob(jp);
4761 jobless = 0;
4762}
4763
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004764/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004765#if !JOBS
4766#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4767#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004768static void
4769forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4770{
4771 TRACE(("In parent shell: child = %d\n", pid));
4772 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004773 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4774 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004775 jobless++;
4776 return;
4777 }
4778#if JOBS
4779 if (mode != FORK_NOJOB && jp->jobctl) {
4780 int pgrp;
4781
4782 if (jp->nprocs == 0)
4783 pgrp = pid;
4784 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004785 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004786 /* This can fail because we are doing it in the child also */
4787 setpgid(pid, pgrp);
4788 }
4789#endif
4790 if (mode == FORK_BG) {
4791 backgndpid = pid; /* set $! */
4792 set_curjob(jp, CUR_RUNNING);
4793 }
4794 if (jp) {
4795 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004796 ps->ps_pid = pid;
4797 ps->ps_status = -1;
4798 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004799#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004800 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004801 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004802#endif
4803 }
4804}
4805
4806static int
4807forkshell(struct job *jp, union node *n, int mode)
4808{
4809 int pid;
4810
4811 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4812 pid = fork();
4813 if (pid < 0) {
4814 TRACE(("Fork failed, errno=%d", errno));
4815 if (jp)
4816 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004817 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004818 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004819 if (pid == 0) {
4820 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004821 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004822 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004823 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004824 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004825 return pid;
4826}
4827
4828/*
4829 * Wait for job to finish.
4830 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004831 * Under job control we have the problem that while a child process
4832 * is running interrupts generated by the user are sent to the child
4833 * but not to the shell. This means that an infinite loop started by
4834 * an interactive user may be hard to kill. With job control turned off,
4835 * an interactive user may place an interactive program inside a loop.
4836 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004837 * these interrupts to also abort the loop. The approach we take here
4838 * is to have the shell ignore interrupt signals while waiting for a
4839 * foreground process to terminate, and then send itself an interrupt
4840 * signal if the child process was terminated by an interrupt signal.
4841 * Unfortunately, some programs want to do a bit of cleanup and then
4842 * exit on interrupt; unless these processes terminate themselves by
4843 * sending a signal to themselves (instead of calling exit) they will
4844 * confuse this approach.
4845 *
4846 * Called with interrupts off.
4847 */
4848static int
4849waitforjob(struct job *jp)
4850{
4851 int st;
4852
4853 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004854
4855 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004856 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004857 /* In non-interactive shells, we _can_ get
4858 * a keyboard signal here and be EINTRed,
4859 * but we just loop back, waiting for command to complete.
4860 *
4861 * man bash:
4862 * "If bash is waiting for a command to complete and receives
4863 * a signal for which a trap has been set, the trap
4864 * will not be executed until the command completes."
4865 *
4866 * Reality is that even if trap is not set, bash
4867 * will not act on the signal until command completes.
4868 * Try this. sleep5intoff.c:
4869 * #include <signal.h>
4870 * #include <unistd.h>
4871 * int main() {
4872 * sigset_t set;
4873 * sigemptyset(&set);
4874 * sigaddset(&set, SIGINT);
4875 * sigaddset(&set, SIGQUIT);
4876 * sigprocmask(SIG_BLOCK, &set, NULL);
4877 * sleep(5);
4878 * return 0;
4879 * }
4880 * $ bash -c './sleep5intoff; echo hi'
4881 * ^C^C^C^C <--- pressing ^C once a second
4882 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004883 * $ bash -c './sleep5intoff; echo hi'
4884 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4885 * $ _
4886 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004887 dowait(DOWAIT_BLOCK, jp);
4888 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004889 INT_ON;
4890
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004891 st = getstatus(jp);
4892#if JOBS
4893 if (jp->jobctl) {
4894 xtcsetpgrp(ttyfd, rootpid);
4895 /*
4896 * This is truly gross.
4897 * If we're doing job control, then we did a TIOCSPGRP which
4898 * caused us (the shell) to no longer be in the controlling
4899 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4900 * intuit from the subprocess exit status whether a SIGINT
4901 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4902 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004903 if (jp->sigint) /* TODO: do the same with all signals */
4904 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004905 }
4906 if (jp->state == JOBDONE)
4907#endif
4908 freejob(jp);
4909 return st;
4910}
4911
4912/*
4913 * return 1 if there are stopped jobs, otherwise 0
4914 */
4915static int
4916stoppedjobs(void)
4917{
4918 struct job *jp;
4919 int retval;
4920
4921 retval = 0;
4922 if (job_warning)
4923 goto out;
4924 jp = curjob;
4925 if (jp && jp->state == JOBSTOPPED) {
4926 out2str("You have stopped jobs.\n");
4927 job_warning = 2;
4928 retval++;
4929 }
4930 out:
4931 return retval;
4932}
4933
4934
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004935/* ============ redir.c
4936 *
4937 * Code for dealing with input/output redirection.
4938 */
4939
4940#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004941#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004942
4943/*
4944 * Open a file in noclobber mode.
4945 * The code was copied from bash.
4946 */
4947static int
4948noclobberopen(const char *fname)
4949{
4950 int r, fd;
4951 struct stat finfo, finfo2;
4952
4953 /*
4954 * If the file exists and is a regular file, return an error
4955 * immediately.
4956 */
4957 r = stat(fname, &finfo);
4958 if (r == 0 && S_ISREG(finfo.st_mode)) {
4959 errno = EEXIST;
4960 return -1;
4961 }
4962
4963 /*
4964 * If the file was not present (r != 0), make sure we open it
4965 * exclusively so that if it is created before we open it, our open
4966 * will fail. Make sure that we do not truncate an existing file.
4967 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4968 * file was not a regular file, we leave O_EXCL off.
4969 */
4970 if (r != 0)
4971 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4972 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4973
4974 /* If the open failed, return the file descriptor right away. */
4975 if (fd < 0)
4976 return fd;
4977
4978 /*
4979 * OK, the open succeeded, but the file may have been changed from a
4980 * non-regular file to a regular file between the stat and the open.
4981 * We are assuming that the O_EXCL open handles the case where FILENAME
4982 * did not exist and is symlinked to an existing file between the stat
4983 * and open.
4984 */
4985
4986 /*
4987 * If we can open it and fstat the file descriptor, and neither check
4988 * revealed that it was a regular file, and the file has not been
4989 * replaced, return the file descriptor.
4990 */
4991 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4992 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4993 return fd;
4994
4995 /* The file has been replaced. badness. */
4996 close(fd);
4997 errno = EEXIST;
4998 return -1;
4999}
5000
5001/*
5002 * Handle here documents. Normally we fork off a process to write the
5003 * data to a pipe. If the document is short, we can stuff the data in
5004 * the pipe without forking.
5005 */
5006/* openhere needs this forward reference */
5007static void expandhere(union node *arg, int fd);
5008static int
5009openhere(union node *redir)
5010{
5011 int pip[2];
5012 size_t len = 0;
5013
5014 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005015 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005016 if (redir->type == NHERE) {
5017 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005018 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005019 full_write(pip[1], redir->nhere.doc->narg.text, len);
5020 goto out;
5021 }
5022 }
5023 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005024 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005025 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005026 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5027 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5028 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5029 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005030 signal(SIGPIPE, SIG_DFL);
5031 if (redir->type == NHERE)
5032 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005033 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005034 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005035 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005036 }
5037 out:
5038 close(pip[1]);
5039 return pip[0];
5040}
5041
5042static int
5043openredirect(union node *redir)
5044{
5045 char *fname;
5046 int f;
5047
5048 switch (redir->nfile.type) {
5049 case NFROM:
5050 fname = redir->nfile.expfname;
5051 f = open(fname, O_RDONLY);
5052 if (f < 0)
5053 goto eopen;
5054 break;
5055 case NFROMTO:
5056 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005057 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005058 if (f < 0)
5059 goto ecreate;
5060 break;
5061 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005062#if ENABLE_ASH_BASH_COMPAT
5063 case NTO2:
5064#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005065 /* Take care of noclobber mode. */
5066 if (Cflag) {
5067 fname = redir->nfile.expfname;
5068 f = noclobberopen(fname);
5069 if (f < 0)
5070 goto ecreate;
5071 break;
5072 }
5073 /* FALLTHROUGH */
5074 case NCLOBBER:
5075 fname = redir->nfile.expfname;
5076 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5077 if (f < 0)
5078 goto ecreate;
5079 break;
5080 case NAPPEND:
5081 fname = redir->nfile.expfname;
5082 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5083 if (f < 0)
5084 goto ecreate;
5085 break;
5086 default:
5087#if DEBUG
5088 abort();
5089#endif
5090 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005091/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005092// case NTOFD:
5093// case NFROMFD:
5094// f = -1;
5095// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005096 case NHERE:
5097 case NXHERE:
5098 f = openhere(redir);
5099 break;
5100 }
5101
5102 return f;
5103 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005104 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005105 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005106 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005107}
5108
5109/*
5110 * Copy a file descriptor to be >= to. Returns -1
5111 * if the source file descriptor is closed, EMPTY if there are no unused
5112 * file descriptors left.
5113 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005114/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5115 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005116enum {
5117 COPYFD_EXACT = (int)~(INT_MAX),
5118 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5119};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005120static int
5121copyfd(int from, int to)
5122{
5123 int newfd;
5124
Denis Vlasenko5a867312008-07-24 19:46:38 +00005125 if (to & COPYFD_EXACT) {
5126 to &= ~COPYFD_EXACT;
5127 /*if (from != to)*/
5128 newfd = dup2(from, to);
5129 } else {
5130 newfd = fcntl(from, F_DUPFD, to);
5131 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005132 if (newfd < 0) {
5133 if (errno == EMFILE)
5134 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005135 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005136 ash_msg_and_raise_error("%d: %m", from);
5137 }
5138 return newfd;
5139}
5140
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005141/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005142struct two_fd_t {
5143 int orig, copy;
5144};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005145struct redirtab {
5146 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005147 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005148 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005149 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005150};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005151#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005152
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005153static int need_to_remember(struct redirtab *rp, int fd)
5154{
5155 int i;
5156
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005157 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005158 return 0;
5159
5160 for (i = 0; i < rp->pair_count; i++) {
5161 if (rp->two_fd[i].orig == fd) {
5162 /* already remembered */
5163 return 0;
5164 }
5165 }
5166 return 1;
5167}
5168
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005169/* "hidden" fd is a fd used to read scripts, or a copy of such */
5170static int is_hidden_fd(struct redirtab *rp, int fd)
5171{
5172 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005173 struct parsefile *pf;
5174
5175 if (fd == -1)
5176 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005177 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005178 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005179 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005180 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005181 * $ ash # running ash interactively
5182 * $ . ./script.sh
5183 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005184 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005185 * it's still ok to use it: "read" builtin uses it,
5186 * why should we cripple "exec" builtin?
5187 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005188 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005189 return 1;
5190 }
5191 pf = pf->prev;
5192 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005193
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005194 if (!rp)
5195 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005196 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005197 fd |= COPYFD_RESTORE;
5198 for (i = 0; i < rp->pair_count; i++) {
5199 if (rp->two_fd[i].copy == fd) {
5200 return 1;
5201 }
5202 }
5203 return 0;
5204}
5205
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005206/*
5207 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5208 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005209 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005210 */
5211/* flags passed to redirect */
5212#define REDIR_PUSH 01 /* save previous values of file descriptors */
5213#define REDIR_SAVEFD2 03 /* set preverrout */
5214static void
5215redirect(union node *redir, int flags)
5216{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005217 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005218 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005219 int i;
5220 int fd;
5221 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005222 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005223
Denis Vlasenko01631112007-12-16 17:20:38 +00005224 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005225 if (!redir) {
5226 return;
5227 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005228
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005229 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005230 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005231 INT_OFF;
5232 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005233 union node *tmp = redir;
5234 do {
5235 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005236#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005237 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005238 sv_pos++;
5239#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005240 tmp = tmp->nfile.next;
5241 } while (tmp);
5242 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005243 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005244 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005245 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005246 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005247 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005248 while (sv_pos > 0) {
5249 sv_pos--;
5250 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5251 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005252 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005253
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005254 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005255 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005256 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005257 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005258 right_fd = redir->ndup.dupfd;
5259 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005260 /* redirect from/to same file descriptor? */
5261 if (right_fd == fd)
5262 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005263 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005264 if (is_hidden_fd(sv, right_fd)) {
5265 errno = EBADF; /* as if it is closed */
5266 ash_msg_and_raise_error("%d: %m", right_fd);
5267 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005268 newfd = -1;
5269 } else {
5270 newfd = openredirect(redir); /* always >= 0 */
5271 if (fd == newfd) {
5272 /* Descriptor wasn't open before redirect.
5273 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005274 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005275 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005276 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005277 continue;
5278 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005279 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005280#if ENABLE_ASH_BASH_COMPAT
5281 redirect_more:
5282#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005283 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005284 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005285 /* Careful to not accidentally "save"
5286 * to the same fd as right side fd in N>&M */
5287 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5288 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005289/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5290 * are closed in popredir() in the child, preventing them from leaking
5291 * into child. (popredir() also cleans up the mess in case of failures)
5292 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005293 if (i == -1) {
5294 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005295 if (i != EBADF) {
5296 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005297 if (newfd >= 0)
5298 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005299 errno = i;
5300 ash_msg_and_raise_error("%d: %m", fd);
5301 /* NOTREACHED */
5302 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005303 /* EBADF: it is not open - good, remember to close it */
5304 remember_to_close:
5305 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005306 } else { /* fd is open, save its copy */
5307 /* "exec fd>&-" should not close fds
5308 * which point to script file(s).
5309 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005310 if (is_hidden_fd(sv, fd))
5311 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005312 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005313 if (fd == 2)
5314 copied_fd2 = i;
5315 sv->two_fd[sv_pos].orig = fd;
5316 sv->two_fd[sv_pos].copy = i;
5317 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005318 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005319 if (newfd < 0) {
5320 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005321 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005322 /* Don't want to trigger debugging */
5323 if (fd != -1)
5324 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005325 } else {
5326 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005327 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005328 } else if (fd != newfd) { /* move newfd to fd */
5329 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005330#if ENABLE_ASH_BASH_COMPAT
5331 if (!(redir->nfile.type == NTO2 && fd == 2))
5332#endif
5333 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005334 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005335#if ENABLE_ASH_BASH_COMPAT
5336 if (redir->nfile.type == NTO2 && fd == 1) {
5337 /* We already redirected it to fd 1, now copy it to 2 */
5338 newfd = 1;
5339 fd = 2;
5340 goto redirect_more;
5341 }
5342#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005343 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005344
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005345 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005346 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5347 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005348}
5349
5350/*
5351 * Undo the effects of the last redirection.
5352 */
5353static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005354popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005355{
5356 struct redirtab *rp;
5357 int i;
5358
Denis Vlasenko01631112007-12-16 17:20:38 +00005359 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005360 return;
5361 INT_OFF;
5362 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005363 for (i = 0; i < rp->pair_count; i++) {
5364 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005365 int copy = rp->two_fd[i].copy;
5366 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005367 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005368 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005369 continue;
5370 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005371 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005372 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005373 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005374 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005375 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005376 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005377 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005378 }
5379 }
5380 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005381 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005382 free(rp);
5383 INT_ON;
5384}
5385
5386/*
5387 * Undo all redirections. Called on error or interrupt.
5388 */
5389
5390/*
5391 * Discard all saved file descriptors.
5392 */
5393static void
5394clearredir(int drop)
5395{
5396 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005397 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005398 if (!redirlist)
5399 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005400 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005401 }
5402}
5403
5404static int
5405redirectsafe(union node *redir, int flags)
5406{
5407 int err;
5408 volatile int saveint;
5409 struct jmploc *volatile savehandler = exception_handler;
5410 struct jmploc jmploc;
5411
5412 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005413 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5414 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005415 if (!err) {
5416 exception_handler = &jmploc;
5417 redirect(redir, flags);
5418 }
5419 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005420 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005421 longjmp(exception_handler->loc, 1);
5422 RESTORE_INT(saveint);
5423 return err;
5424}
5425
5426
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005427/* ============ Routines to expand arguments to commands
5428 *
5429 * We have to deal with backquotes, shell variables, and file metacharacters.
5430 */
5431
Mike Frysinger98c52642009-04-02 10:02:37 +00005432#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005433static arith_t
5434ash_arith(const char *s)
5435{
5436 arith_eval_hooks_t math_hooks;
5437 arith_t result;
5438 int errcode = 0;
5439
5440 math_hooks.lookupvar = lookupvar;
Denys Vlasenko03dad222010-01-12 23:29:57 +01005441 math_hooks.setvar = setvar2;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005442 math_hooks.endofname = endofname;
5443
5444 INT_OFF;
5445 result = arith(s, &errcode, &math_hooks);
5446 if (errcode < 0) {
5447 if (errcode == -3)
5448 ash_msg_and_raise_error("exponent less than 0");
5449 if (errcode == -2)
5450 ash_msg_and_raise_error("divide by zero");
5451 if (errcode == -5)
5452 ash_msg_and_raise_error("expression recursion loop detected");
5453 raise_error_syntax(s);
5454 }
5455 INT_ON;
5456
5457 return result;
5458}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005459#endif
5460
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005461/*
5462 * expandarg flags
5463 */
5464#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5465#define EXP_TILDE 0x2 /* do normal tilde expansion */
5466#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5467#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5468#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5469#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5470#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5471#define EXP_WORD 0x80 /* expand word in parameter expansion */
5472#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5473/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005474 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005475 */
5476#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5477#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5478#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5479#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5480#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5481
5482/*
5483 * Structure specifying which parts of the string should be searched
5484 * for IFS characters.
5485 */
5486struct ifsregion {
5487 struct ifsregion *next; /* next region in list */
5488 int begoff; /* offset of start of region */
5489 int endoff; /* offset of end of region */
5490 int nulonly; /* search for nul bytes only */
5491};
5492
5493struct arglist {
5494 struct strlist *list;
5495 struct strlist **lastp;
5496};
5497
5498/* output of current string */
5499static char *expdest;
5500/* list of back quote expressions */
5501static struct nodelist *argbackq;
5502/* first struct in list of ifs regions */
5503static struct ifsregion ifsfirst;
5504/* last struct in list */
5505static struct ifsregion *ifslastp;
5506/* holds expanded arg list */
5507static struct arglist exparg;
5508
5509/*
5510 * Our own itoa().
5511 */
5512static int
5513cvtnum(arith_t num)
5514{
5515 int len;
5516
5517 expdest = makestrspace(32, expdest);
Mike Frysinger98c52642009-04-02 10:02:37 +00005518 len = fmtstr(expdest, 32, arith_t_fmt, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005519 STADJUST(len, expdest);
5520 return len;
5521}
5522
5523static size_t
5524esclen(const char *start, const char *p)
5525{
5526 size_t esc = 0;
5527
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005528 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005529 esc++;
5530 }
5531 return esc;
5532}
5533
5534/*
5535 * Remove any CTLESC characters from a string.
5536 */
5537static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005538rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005539{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005540 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005541
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005542 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005543 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005544 unsigned protect_against_glob;
5545 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005546
5547 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005548 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005549 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005550
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005551 q = p;
5552 r = str;
5553 if (flag & RMESCAPE_ALLOC) {
5554 size_t len = p - str;
5555 size_t fulllen = len + strlen(p) + 1;
5556
5557 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005558 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005559 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005560 /* p and str may be invalidated by makestrspace */
5561 str = (char *)stackblock() + strloc;
5562 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005563 } else if (flag & RMESCAPE_HEAP) {
5564 r = ckmalloc(fulllen);
5565 } else {
5566 r = stalloc(fulllen);
5567 }
5568 q = r;
5569 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005570 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005571 }
5572 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005573
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005574 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5575 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005576 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005577 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005578 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005579// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5580// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5581// Note: both inquotes and protect_against_glob only affect whether
5582// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005583 inquotes = ~inquotes;
5584 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005585 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005586 continue;
5587 }
5588 if (*p == '\\') {
5589 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005590 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005591 goto copy;
5592 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005593 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005594 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005595 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596 *q++ = '\\';
5597 }
5598 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005599 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005600 copy:
5601 *q++ = *p++;
5602 }
5603 *q = '\0';
5604 if (flag & RMESCAPE_GROW) {
5605 expdest = r;
5606 STADJUST(q - r + 1, expdest);
5607 }
5608 return r;
5609}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005610#define pmatch(a, b) !fnmatch((a), (b), 0)
5611
5612/*
5613 * Prepare a pattern for a expmeta (internal glob(3)) call.
5614 *
5615 * Returns an stalloced string.
5616 */
5617static char *
5618preglob(const char *pattern, int quoted, int flag)
5619{
5620 flag |= RMESCAPE_GLOB;
5621 if (quoted) {
5622 flag |= RMESCAPE_QUOTED;
5623 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005624 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005625}
5626
5627/*
5628 * Put a string on the stack.
5629 */
5630static void
5631memtodest(const char *p, size_t len, int syntax, int quotes)
5632{
5633 char *q = expdest;
5634
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005635 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005636
5637 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005638 unsigned char c = *p++;
5639 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005640 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005641 if (quotes) {
5642 int n = SIT(c, syntax);
5643 if (n == CCTL || n == CBACK)
5644 USTPUTC(CTLESC, q);
5645 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005646 USTPUTC(c, q);
5647 }
5648
5649 expdest = q;
5650}
5651
5652static void
5653strtodest(const char *p, int syntax, int quotes)
5654{
5655 memtodest(p, strlen(p), syntax, quotes);
5656}
5657
5658/*
5659 * Record the fact that we have to scan this region of the
5660 * string for IFS characters.
5661 */
5662static void
5663recordregion(int start, int end, int nulonly)
5664{
5665 struct ifsregion *ifsp;
5666
5667 if (ifslastp == NULL) {
5668 ifsp = &ifsfirst;
5669 } else {
5670 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005671 ifsp = ckzalloc(sizeof(*ifsp));
5672 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005673 ifslastp->next = ifsp;
5674 INT_ON;
5675 }
5676 ifslastp = ifsp;
5677 ifslastp->begoff = start;
5678 ifslastp->endoff = end;
5679 ifslastp->nulonly = nulonly;
5680}
5681
5682static void
5683removerecordregions(int endoff)
5684{
5685 if (ifslastp == NULL)
5686 return;
5687
5688 if (ifsfirst.endoff > endoff) {
5689 while (ifsfirst.next != NULL) {
5690 struct ifsregion *ifsp;
5691 INT_OFF;
5692 ifsp = ifsfirst.next->next;
5693 free(ifsfirst.next);
5694 ifsfirst.next = ifsp;
5695 INT_ON;
5696 }
5697 if (ifsfirst.begoff > endoff)
5698 ifslastp = NULL;
5699 else {
5700 ifslastp = &ifsfirst;
5701 ifsfirst.endoff = endoff;
5702 }
5703 return;
5704 }
5705
5706 ifslastp = &ifsfirst;
5707 while (ifslastp->next && ifslastp->next->begoff < endoff)
5708 ifslastp=ifslastp->next;
5709 while (ifslastp->next != NULL) {
5710 struct ifsregion *ifsp;
5711 INT_OFF;
5712 ifsp = ifslastp->next->next;
5713 free(ifslastp->next);
5714 ifslastp->next = ifsp;
5715 INT_ON;
5716 }
5717 if (ifslastp->endoff > endoff)
5718 ifslastp->endoff = endoff;
5719}
5720
5721static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005722exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005723{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005724 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005725 char *name;
5726 struct passwd *pw;
5727 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005728 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005729 int startloc;
5730
5731 name = p + 1;
5732
5733 while ((c = *++p) != '\0') {
5734 switch (c) {
5735 case CTLESC:
5736 return startp;
5737 case CTLQUOTEMARK:
5738 return startp;
5739 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005740 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005741 goto done;
5742 break;
5743 case '/':
5744 case CTLENDVAR:
5745 goto done;
5746 }
5747 }
5748 done:
5749 *p = '\0';
5750 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005751 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005752 } else {
5753 pw = getpwnam(name);
5754 if (pw == NULL)
5755 goto lose;
5756 home = pw->pw_dir;
5757 }
5758 if (!home || !*home)
5759 goto lose;
5760 *p = c;
5761 startloc = expdest - (char *)stackblock();
5762 strtodest(home, SQSYNTAX, quotes);
5763 recordregion(startloc, expdest - (char *)stackblock(), 0);
5764 return p;
5765 lose:
5766 *p = c;
5767 return startp;
5768}
5769
5770/*
5771 * Execute a command inside back quotes. If it's a builtin command, we
5772 * want to save its output in a block obtained from malloc. Otherwise
5773 * we fork off a subprocess and get the output of the command via a pipe.
5774 * Should be called with interrupts off.
5775 */
5776struct backcmd { /* result of evalbackcmd */
5777 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005778 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005779 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005780 struct job *jp; /* job structure for command */
5781};
5782
5783/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005784static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005785#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005786static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005787
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005788static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005789evalbackcmd(union node *n, struct backcmd *result)
5790{
5791 int saveherefd;
5792
5793 result->fd = -1;
5794 result->buf = NULL;
5795 result->nleft = 0;
5796 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005797 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005798 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005799
5800 saveherefd = herefd;
5801 herefd = -1;
5802
5803 {
5804 int pip[2];
5805 struct job *jp;
5806
5807 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005808 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005809 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005810 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5811 FORCE_INT_ON;
5812 close(pip[0]);
5813 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005814 /*close(1);*/
5815 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005816 close(pip[1]);
5817 }
5818 eflag = 0;
5819 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5820 /* NOTREACHED */
5821 }
5822 close(pip[1]);
5823 result->fd = pip[0];
5824 result->jp = jp;
5825 }
5826 herefd = saveherefd;
5827 out:
5828 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5829 result->fd, result->buf, result->nleft, result->jp));
5830}
5831
5832/*
5833 * Expand stuff in backwards quotes.
5834 */
5835static void
5836expbackq(union node *cmd, int quoted, int quotes)
5837{
5838 struct backcmd in;
5839 int i;
5840 char buf[128];
5841 char *p;
5842 char *dest;
5843 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005844 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005845 struct stackmark smark;
5846
5847 INT_OFF;
5848 setstackmark(&smark);
5849 dest = expdest;
5850 startloc = dest - (char *)stackblock();
5851 grabstackstr(dest);
5852 evalbackcmd(cmd, &in);
5853 popstackmark(&smark);
5854
5855 p = in.buf;
5856 i = in.nleft;
5857 if (i == 0)
5858 goto read;
5859 for (;;) {
5860 memtodest(p, i, syntax, quotes);
5861 read:
5862 if (in.fd < 0)
5863 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005864 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005865 TRACE(("expbackq: read returns %d\n", i));
5866 if (i <= 0)
5867 break;
5868 p = buf;
5869 }
5870
Denis Vlasenko60818682007-09-28 22:07:23 +00005871 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005872 if (in.fd >= 0) {
5873 close(in.fd);
5874 back_exitstatus = waitforjob(in.jp);
5875 }
5876 INT_ON;
5877
5878 /* Eat all trailing newlines */
5879 dest = expdest;
5880 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5881 STUNPUTC(dest);
5882 expdest = dest;
5883
5884 if (quoted == 0)
5885 recordregion(startloc, dest - (char *)stackblock(), 0);
5886 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5887 (dest - (char *)stackblock()) - startloc,
5888 (dest - (char *)stackblock()) - startloc,
5889 stackblock() + startloc));
5890}
5891
Mike Frysinger98c52642009-04-02 10:02:37 +00005892#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005893/*
5894 * Expand arithmetic expression. Backup to start of expression,
5895 * evaluate, place result in (backed up) result, adjust string position.
5896 */
5897static void
5898expari(int quotes)
5899{
5900 char *p, *start;
5901 int begoff;
5902 int flag;
5903 int len;
5904
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005905 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005906
5907 /*
5908 * This routine is slightly over-complicated for
5909 * efficiency. Next we scan backwards looking for the
5910 * start of arithmetic.
5911 */
5912 start = stackblock();
5913 p = expdest - 1;
5914 *p = '\0';
5915 p--;
5916 do {
5917 int esc;
5918
Denys Vlasenkocd716832009-11-28 22:14:02 +01005919 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005920 p--;
5921#if DEBUG
5922 if (p < start) {
5923 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5924 }
5925#endif
5926 }
5927
5928 esc = esclen(start, p);
5929 if (!(esc % 2)) {
5930 break;
5931 }
5932
5933 p -= esc + 1;
5934 } while (1);
5935
5936 begoff = p - start;
5937
5938 removerecordregions(begoff);
5939
5940 flag = p[1];
5941
5942 expdest = p;
5943
5944 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005945 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005946
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005947 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005948
5949 if (flag != '"')
5950 recordregion(begoff, begoff + len, 0);
5951}
5952#endif
5953
5954/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005955static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005956
5957/*
5958 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5959 * characters to allow for further processing. Otherwise treat
5960 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005961 *
5962 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5963 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5964 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005965 */
5966static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005967argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005968{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005969 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005970 '=',
5971 ':',
5972 CTLQUOTEMARK,
5973 CTLENDVAR,
5974 CTLESC,
5975 CTLVAR,
5976 CTLBACKQ,
5977 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00005978#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005979 CTLENDARI,
5980#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01005981 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005982 };
5983 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005984 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
5985 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005986 int inquotes;
5987 size_t length;
5988 int startloc;
5989
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005990 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005991 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005992 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005993 reject++;
5994 }
5995 inquotes = 0;
5996 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005997 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005998 char *q;
5999
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006000 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006001 tilde:
6002 q = p;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006003 if (*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006004 q++;
6005 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006006 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006007 }
6008 start:
6009 startloc = expdest - (char *)stackblock();
6010 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006011 unsigned char c;
6012
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006013 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006014 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006015 if (c) {
6016 if (!(c & 0x80)
Mike Frysinger98c52642009-04-02 10:02:37 +00006017#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006018 || c == CTLENDARI
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006019#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006020 ) {
6021 /* c == '=' || c == ':' || c == CTLENDARI */
6022 length++;
6023 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006024 }
6025 if (length > 0) {
6026 int newloc;
6027 expdest = stack_nputstr(p, length, expdest);
6028 newloc = expdest - (char *)stackblock();
6029 if (breakall && !inquotes && newloc > startloc) {
6030 recordregion(startloc, newloc, 0);
6031 }
6032 startloc = newloc;
6033 }
6034 p += length + 1;
6035 length = 0;
6036
6037 switch (c) {
6038 case '\0':
6039 goto breakloop;
6040 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006041 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006042 p--;
6043 continue;
6044 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006045 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006046 reject++;
6047 /* fall through */
6048 case ':':
6049 /*
6050 * sort of a hack - expand tildes in variable
6051 * assignments (after the first '=' and after ':'s).
6052 */
6053 if (*--p == '~') {
6054 goto tilde;
6055 }
6056 continue;
6057 }
6058
6059 switch (c) {
6060 case CTLENDVAR: /* ??? */
6061 goto breakloop;
6062 case CTLQUOTEMARK:
6063 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006064 if (!inquotes
6065 && memcmp(p, dolatstr, 4) == 0
6066 && ( p[4] == CTLQUOTEMARK
6067 || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK)
6068 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006069 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006070 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006071 goto start;
6072 }
6073 inquotes = !inquotes;
6074 addquote:
6075 if (quotes) {
6076 p--;
6077 length++;
6078 startloc++;
6079 }
6080 break;
6081 case CTLESC:
6082 startloc++;
6083 length++;
6084 goto addquote;
6085 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006086 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006087 goto start;
6088 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006089 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090 case CTLBACKQ|CTLQUOTE:
6091 expbackq(argbackq->n, c, quotes);
6092 argbackq = argbackq->next;
6093 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006094#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006095 case CTLENDARI:
6096 p--;
6097 expari(quotes);
6098 goto start;
6099#endif
6100 }
6101 }
6102 breakloop:
6103 ;
6104}
6105
6106static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006107scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006108 int zero)
6109{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006110// This commented out code was added by James Simmons <jsimmons@infradead.org>
6111// as part of a larger change when he added support for ${var/a/b}.
6112// However, it broke # and % operators:
6113//
6114//var=ababcdcd
6115// ok bad
6116//echo ${var#ab} abcdcd abcdcd
6117//echo ${var##ab} abcdcd abcdcd
6118//echo ${var#a*b} abcdcd ababcdcd (!)
6119//echo ${var##a*b} cdcd cdcd
6120//echo ${var#?} babcdcd ababcdcd (!)
6121//echo ${var##?} babcdcd babcdcd
6122//echo ${var#*} ababcdcd babcdcd (!)
6123//echo ${var##*}
6124//echo ${var%cd} ababcd ababcd
6125//echo ${var%%cd} ababcd abab (!)
6126//echo ${var%c*d} ababcd ababcd
6127//echo ${var%%c*d} abab ababcdcd (!)
6128//echo ${var%?} ababcdc ababcdc
6129//echo ${var%%?} ababcdc ababcdcd (!)
6130//echo ${var%*} ababcdcd ababcdcd
6131//echo ${var%%*}
6132//
6133// Commenting it back out helped. Remove it completely if it really
6134// is not needed.
6135
6136 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006137 char c;
6138
6139 loc = startp;
6140 loc2 = rmesc;
6141 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006142 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006143 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006144
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006145 c = *loc2;
6146 if (zero) {
6147 *loc2 = '\0';
6148 s = rmesc;
6149 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006150 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006151
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006152// // chop off end if its '*'
6153// full = strrchr(str, '*');
6154// if (full && full != str)
6155// match--;
6156//
6157// // If str starts with '*' replace with s.
6158// if ((*str == '*') && strlen(s) >= match) {
6159// full = xstrdup(s);
6160// strncpy(full+strlen(s)-match+1, str+1, match-1);
6161// } else
6162// full = xstrndup(str, match);
6163// match = strncmp(s, full, strlen(full));
6164// free(full);
6165//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00006167 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006168 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006169 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006170 loc++;
6171 loc++;
6172 loc2++;
6173 } while (c);
6174 return 0;
6175}
6176
6177static char *
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006178scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006179{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006180#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6181 int try2optimize = match_at_start;
6182#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006183 int esc = 0;
6184 char *loc;
6185 char *loc2;
6186
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006187 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6188 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6189 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6190 * Logic:
6191 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6192 * and on each iteration they go back two/one char until they reach the beginning.
6193 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6194 */
6195 /* TODO: document in what other circumstances we are called. */
6196
6197 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006198 int match;
6199 char c = *loc2;
6200 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006201 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006202 *loc2 = '\0';
6203 s = rmesc;
6204 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006205 match = pmatch(pattern, s);
6206 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006207 *loc2 = c;
6208 if (match)
6209 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006210#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6211 if (try2optimize) {
6212 /* Maybe we can optimize this:
6213 * if pattern ends with unescaped *, we can avoid checking
6214 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6215 * it wont match truncated "raw_value_of_" strings too.
6216 */
6217 unsigned plen = strlen(pattern);
6218 /* Does it end with "*"? */
6219 if (plen != 0 && pattern[--plen] == '*') {
6220 /* "xxxx*" is not escaped */
6221 /* "xxx\*" is escaped */
6222 /* "xx\\*" is not escaped */
6223 /* "x\\\*" is escaped */
6224 int slashes = 0;
6225 while (plen != 0 && pattern[--plen] == '\\')
6226 slashes++;
6227 if (!(slashes & 1))
6228 break; /* ends with unescaped "*" */
6229 }
6230 try2optimize = 0;
6231 }
6232#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006233 loc--;
6234 if (quotes) {
6235 if (--esc < 0) {
6236 esc = esclen(startp, loc);
6237 }
6238 if (esc % 2) {
6239 esc--;
6240 loc--;
6241 }
6242 }
6243 }
6244 return 0;
6245}
6246
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006247static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006248static void
6249varunset(const char *end, const char *var, const char *umsg, int varflags)
6250{
6251 const char *msg;
6252 const char *tail;
6253
6254 tail = nullstr;
6255 msg = "parameter not set";
6256 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006257 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006258 if (varflags & VSNUL)
6259 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006260 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006261 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006262 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006263 }
6264 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
6265}
6266
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006267#if ENABLE_ASH_BASH_COMPAT
6268static char *
6269parse_sub_pattern(char *arg, int inquotes)
6270{
6271 char *idx, *repl = NULL;
6272 unsigned char c;
6273
Denis Vlasenko2659c632008-06-14 06:04:59 +00006274 idx = arg;
6275 while (1) {
6276 c = *arg;
6277 if (!c)
6278 break;
6279 if (c == '/') {
6280 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006281 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006282 repl = idx + 1;
6283 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006284 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006285 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006286 *idx++ = c;
6287 if (!inquotes && c == '\\' && arg[1] == '\\')
6288 arg++; /* skip both \\, not just first one */
6289 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006290 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006291 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006292
6293 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006294}
6295#endif /* ENABLE_ASH_BASH_COMPAT */
6296
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006297static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006298subevalvar(char *p, char *str, int strloc, int subtype,
6299 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006300{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006301 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006302 char *startp;
6303 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006304 char *rmesc, *rmescend;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006305 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006306 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006307 int saveherefd = herefd;
6308 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006309 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006310 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006311
6312 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006313 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6314 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006315 STPUTC('\0', expdest);
6316 herefd = saveherefd;
6317 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006318 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006319
6320 switch (subtype) {
6321 case VSASSIGN:
6322 setvar(str, startp, 0);
6323 amount = startp - expdest;
6324 STADJUST(amount, expdest);
6325 return startp;
6326
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006327#if ENABLE_ASH_BASH_COMPAT
6328 case VSSUBSTR:
6329 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006330 /* Read POS in ${var:POS:LEN} */
6331 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006332 len = str - startp - 1;
6333
6334 /* *loc != '\0', guaranteed by parser */
6335 if (quotes) {
6336 char *ptr;
6337
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006338 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006339 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006340 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006341 len--;
6342 ptr++;
6343 }
6344 }
6345 }
6346 orig_len = len;
6347
6348 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006349 /* ${var::LEN} */
6350 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006351 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006352 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006353 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006354 while (*loc && *loc != ':') {
6355 /* TODO?
6356 * bash complains on: var=qwe; echo ${var:1a:123}
6357 if (!isdigit(*loc))
6358 ash_msg_and_raise_error(msg_illnum, str);
6359 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006360 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006361 }
6362 if (*loc++ == ':') {
6363 len = number(loc);
6364 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006365 }
6366 if (pos >= orig_len) {
6367 pos = 0;
6368 len = 0;
6369 }
6370 if (len > (orig_len - pos))
6371 len = orig_len - pos;
6372
6373 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006374 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006375 str++;
6376 }
6377 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006378 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006379 *loc++ = *str++;
6380 *loc++ = *str++;
6381 }
6382 *loc = '\0';
6383 amount = loc - expdest;
6384 STADJUST(amount, expdest);
6385 return loc;
6386#endif
6387
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006388 case VSQUESTION:
6389 varunset(p, str, startp, varflags);
6390 /* NOTREACHED */
6391 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006392 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006393
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006394 /* We'll comeback here if we grow the stack while handling
6395 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6396 * stack will need rebasing, and we'll need to remove our work
6397 * areas each time
6398 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006399 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006400
6401 amount = expdest - ((char *)stackblock() + resetloc);
6402 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006403 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006404
6405 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006406 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006407 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006408 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006409 if (rmesc != startp) {
6410 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006411 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006412 }
6413 }
6414 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006415 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006416 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006417 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006418
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006419#if ENABLE_ASH_BASH_COMPAT
6420 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006421 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006422
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006423 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006424 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6425 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006426 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006427 }
6428
6429 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006430 if (str[0] == '\0')
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006431 return 0;
6432
6433 len = 0;
6434 idx = startp;
6435 end = str - 1;
6436 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006437 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006438 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6439 if (!loc) {
6440 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006441 char *restart_detect = stackblock();
6442 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006443 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006444 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006445 idx++;
6446 len++;
6447 STPUTC(*idx, expdest);
6448 }
6449 if (stackblock() != restart_detect)
6450 goto restart;
6451 idx++;
6452 len++;
6453 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006454 /* continue; - prone to quadratic behavior, smarter code: */
6455 if (idx >= end)
6456 break;
6457 if (str[0] == '*') {
6458 /* Pattern is "*foo". If "*foo" does not match "long_string",
6459 * it would never match "ong_string" etc, no point in trying.
6460 */
6461 goto skip_matching;
6462 }
6463 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006464 }
6465
6466 if (subtype == VSREPLACEALL) {
6467 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006468 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006469 idx++;
6470 idx++;
6471 rmesc++;
6472 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006473 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006474 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006475 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006476
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006477 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006478 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006479 if (quotes && *loc == '\\') {
6480 STPUTC(CTLESC, expdest);
6481 len++;
6482 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006483 STPUTC(*loc, expdest);
6484 if (stackblock() != restart_detect)
6485 goto restart;
6486 len++;
6487 }
6488
6489 if (subtype == VSREPLACE) {
6490 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006491 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006492 if (quotes && *idx == '\\') {
6493 STPUTC(CTLESC, expdest);
6494 len++;
6495 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006496 STPUTC(*idx, expdest);
6497 if (stackblock() != restart_detect)
6498 goto restart;
6499 len++;
6500 idx++;
6501 }
6502 break;
6503 }
6504 }
6505
6506 /* We've put the replaced text into a buffer at workloc, now
6507 * move it to the right place and adjust the stack.
6508 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006509 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006510 startp = (char *)stackblock() + startloc;
6511 memmove(startp, (char *)stackblock() + workloc, len + 1);
6512 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006513 STADJUST(-amount, expdest);
6514 return startp;
6515 }
6516#endif /* ENABLE_ASH_BASH_COMPAT */
6517
6518 subtype -= VSTRIMRIGHT;
6519#if DEBUG
6520 if (subtype < 0 || subtype > 7)
6521 abort();
6522#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006523 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006524 zero = subtype >> 1;
6525 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6526 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6527
6528 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6529 if (loc) {
6530 if (zero) {
6531 memmove(startp, loc, str - loc);
6532 loc = startp + (str - loc) - 1;
6533 }
6534 *loc = '\0';
6535 amount = loc - expdest;
6536 STADJUST(amount, expdest);
6537 }
6538 return loc;
6539}
6540
6541/*
6542 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006543 * name parameter (examples):
6544 * ash -c 'echo $1' name:'1='
6545 * ash -c 'echo $qwe' name:'qwe='
6546 * ash -c 'echo $$' name:'$='
6547 * ash -c 'echo ${$}' name:'$='
6548 * ash -c 'echo ${$##q}' name:'$=q'
6549 * ash -c 'echo ${#$}' name:'$='
6550 * note: examples with bad shell syntax:
6551 * ash -c 'echo ${#$1}' name:'$=1'
6552 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006553 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006554static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006555varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006556{
Mike Frysinger98c52642009-04-02 10:02:37 +00006557 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006558 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006559 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006560 int sepq = 0;
6561 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006562 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006563 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006564 int quoted = varflags & VSQUOTE;
6565 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006566
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006567 switch (*name) {
6568 case '$':
6569 num = rootpid;
6570 goto numvar;
6571 case '?':
6572 num = exitstatus;
6573 goto numvar;
6574 case '#':
6575 num = shellparam.nparam;
6576 goto numvar;
6577 case '!':
6578 num = backgndpid;
6579 if (num == 0)
6580 return -1;
6581 numvar:
6582 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006583 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006584 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006585 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006586 for (i = NOPTS - 1; i >= 0; i--) {
6587 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006588 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006589 len++;
6590 }
6591 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006592 check_1char_name:
6593#if 0
6594 /* handles cases similar to ${#$1} */
6595 if (name[2] != '\0')
6596 raise_error_syntax("bad substitution");
6597#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006598 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006599 case '@': {
6600 char **ap;
6601 int sep;
6602
6603 if (quoted && (flags & EXP_FULL)) {
6604 /* note: this is not meant as PEOF value */
6605 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006606 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006607 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006608 /* fall through */
6609 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006610 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006611 i = SIT(sep, syntax);
6612 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006613 sepq = 1;
6614 param:
6615 ap = shellparam.p;
6616 if (!ap)
6617 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006618 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006619 size_t partlen;
6620
6621 partlen = strlen(p);
6622 len += partlen;
6623
6624 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6625 memtodest(p, partlen, syntax, quotes);
6626
6627 if (*ap && sep) {
6628 char *q;
6629
6630 len++;
6631 if (subtype == VSPLUS || subtype == VSLENGTH) {
6632 continue;
6633 }
6634 q = expdest;
6635 if (sepq)
6636 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006637 /* note: may put NUL despite sep != 0
6638 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006639 STPUTC(sep, q);
6640 expdest = q;
6641 }
6642 }
6643 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006644 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006645 case '0':
6646 case '1':
6647 case '2':
6648 case '3':
6649 case '4':
6650 case '5':
6651 case '6':
6652 case '7':
6653 case '8':
6654 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006655 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006656 if (num < 0 || num > shellparam.nparam)
6657 return -1;
6658 p = num ? shellparam.p[num - 1] : arg0;
6659 goto value;
6660 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006661 /* NB: name has form "VAR=..." */
6662
6663 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6664 * which should be considered before we check variables. */
6665 if (var_str_list) {
6666 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6667 p = NULL;
6668 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006669 char *str, *eq;
6670 str = var_str_list->text;
6671 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006672 if (!eq) /* stop at first non-assignment */
6673 break;
6674 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006675 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006676 && strncmp(str, name, name_len) == 0
6677 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006678 p = eq;
6679 /* goto value; - WRONG! */
6680 /* think "A=1 A=2 B=$A" */
6681 }
6682 var_str_list = var_str_list->next;
6683 } while (var_str_list);
6684 if (p)
6685 goto value;
6686 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006687 p = lookupvar(name);
6688 value:
6689 if (!p)
6690 return -1;
6691
6692 len = strlen(p);
6693 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6694 memtodest(p, len, syntax, quotes);
6695 return len;
6696 }
6697
6698 if (subtype == VSPLUS || subtype == VSLENGTH)
6699 STADJUST(-len, expdest);
6700 return len;
6701}
6702
6703/*
6704 * Expand a variable, and return a pointer to the next character in the
6705 * input string.
6706 */
6707static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006708evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006709{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006710 char varflags;
6711 char subtype;
6712 char quoted;
6713 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006714 char *var;
6715 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006716 int startloc;
6717 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006718
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006719 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006720 subtype = varflags & VSTYPE;
6721 quoted = varflags & VSQUOTE;
6722 var = p;
6723 easy = (!quoted || (*var == '@' && shellparam.nparam));
6724 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006725 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006726
6727 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006728 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006729 if (varflags & VSNUL)
6730 varlen--;
6731
6732 if (subtype == VSPLUS) {
6733 varlen = -1 - varlen;
6734 goto vsplus;
6735 }
6736
6737 if (subtype == VSMINUS) {
6738 vsplus:
6739 if (varlen < 0) {
6740 argstr(
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006741 p, flags | EXP_TILDE |
6742 (quoted ? EXP_QWORD : EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006743 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006744 );
6745 goto end;
6746 }
6747 if (easy)
6748 goto record;
6749 goto end;
6750 }
6751
6752 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6753 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006754 if (subevalvar(p, var, /* strloc: */ 0,
6755 subtype, startloc, varflags,
6756 /* quotes: */ 0,
6757 var_str_list)
6758 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006759 varflags &= ~VSNUL;
6760 /*
6761 * Remove any recorded regions beyond
6762 * start of variable
6763 */
6764 removerecordregions(startloc);
6765 goto again;
6766 }
6767 goto end;
6768 }
6769 if (easy)
6770 goto record;
6771 goto end;
6772 }
6773
6774 if (varlen < 0 && uflag)
6775 varunset(p, var, 0, 0);
6776
6777 if (subtype == VSLENGTH) {
6778 cvtnum(varlen > 0 ? varlen : 0);
6779 goto record;
6780 }
6781
6782 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006783 if (easy)
6784 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006785 goto end;
6786 }
6787
6788#if DEBUG
6789 switch (subtype) {
6790 case VSTRIMLEFT:
6791 case VSTRIMLEFTMAX:
6792 case VSTRIMRIGHT:
6793 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006794#if ENABLE_ASH_BASH_COMPAT
6795 case VSSUBSTR:
6796 case VSREPLACE:
6797 case VSREPLACEALL:
6798#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006799 break;
6800 default:
6801 abort();
6802 }
6803#endif
6804
6805 if (varlen >= 0) {
6806 /*
6807 * Terminate the string and start recording the pattern
6808 * right after it
6809 */
6810 STPUTC('\0', expdest);
6811 patloc = expdest - (char *)stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006812 if (NULL == subevalvar(p, /* str: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006813 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006814//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006815 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006816 var_str_list)
6817 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006818 int amount = expdest - (
6819 (char *)stackblock() + patloc - 1
6820 );
6821 STADJUST(-amount, expdest);
6822 }
6823 /* Remove any recorded regions beyond start of variable */
6824 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006825 record:
6826 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006827 }
6828
6829 end:
6830 if (subtype != VSNORMAL) { /* skip to end of alternative */
6831 int nesting = 1;
6832 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006833 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006834 if (c == CTLESC)
6835 p++;
6836 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6837 if (varlen >= 0)
6838 argbackq = argbackq->next;
6839 } else if (c == CTLVAR) {
6840 if ((*p++ & VSTYPE) != VSNORMAL)
6841 nesting++;
6842 } else if (c == CTLENDVAR) {
6843 if (--nesting == 0)
6844 break;
6845 }
6846 }
6847 }
6848 return p;
6849}
6850
6851/*
6852 * Break the argument string into pieces based upon IFS and add the
6853 * strings to the argument list. The regions of the string to be
6854 * searched for IFS characters have been stored by recordregion.
6855 */
6856static void
6857ifsbreakup(char *string, struct arglist *arglist)
6858{
6859 struct ifsregion *ifsp;
6860 struct strlist *sp;
6861 char *start;
6862 char *p;
6863 char *q;
6864 const char *ifs, *realifs;
6865 int ifsspc;
6866 int nulonly;
6867
6868 start = string;
6869 if (ifslastp != NULL) {
6870 ifsspc = 0;
6871 nulonly = 0;
6872 realifs = ifsset() ? ifsval() : defifs;
6873 ifsp = &ifsfirst;
6874 do {
6875 p = string + ifsp->begoff;
6876 nulonly = ifsp->nulonly;
6877 ifs = nulonly ? nullstr : realifs;
6878 ifsspc = 0;
6879 while (p < string + ifsp->endoff) {
6880 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006881 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006882 p++;
6883 if (!strchr(ifs, *p)) {
6884 p++;
6885 continue;
6886 }
6887 if (!nulonly)
6888 ifsspc = (strchr(defifs, *p) != NULL);
6889 /* Ignore IFS whitespace at start */
6890 if (q == start && ifsspc) {
6891 p++;
6892 start = p;
6893 continue;
6894 }
6895 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006896 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006897 sp->text = start;
6898 *arglist->lastp = sp;
6899 arglist->lastp = &sp->next;
6900 p++;
6901 if (!nulonly) {
6902 for (;;) {
6903 if (p >= string + ifsp->endoff) {
6904 break;
6905 }
6906 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006907 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006908 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006909 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006910 p = q;
6911 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006912 }
6913 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006914 if (ifsspc) {
6915 p++;
6916 ifsspc = 0;
6917 } else {
6918 p = q;
6919 break;
6920 }
6921 } else
6922 p++;
6923 }
6924 }
6925 start = p;
6926 } /* while */
6927 ifsp = ifsp->next;
6928 } while (ifsp != NULL);
6929 if (nulonly)
6930 goto add;
6931 }
6932
6933 if (!*start)
6934 return;
6935
6936 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006937 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938 sp->text = start;
6939 *arglist->lastp = sp;
6940 arglist->lastp = &sp->next;
6941}
6942
6943static void
6944ifsfree(void)
6945{
6946 struct ifsregion *p;
6947
6948 INT_OFF;
6949 p = ifsfirst.next;
6950 do {
6951 struct ifsregion *ifsp;
6952 ifsp = p->next;
6953 free(p);
6954 p = ifsp;
6955 } while (p);
6956 ifslastp = NULL;
6957 ifsfirst.next = NULL;
6958 INT_ON;
6959}
6960
6961/*
6962 * Add a file name to the list.
6963 */
6964static void
6965addfname(const char *name)
6966{
6967 struct strlist *sp;
6968
Denis Vlasenko597906c2008-02-20 16:38:54 +00006969 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006970 sp->text = ststrdup(name);
6971 *exparg.lastp = sp;
6972 exparg.lastp = &sp->next;
6973}
6974
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006975/*
6976 * Do metacharacter (i.e. *, ?, [...]) expansion.
6977 */
6978static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006979expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006980{
6981 char *p;
6982 const char *cp;
6983 char *start;
6984 char *endname;
6985 int metaflag;
6986 struct stat statb;
6987 DIR *dirp;
6988 struct dirent *dp;
6989 int atend;
6990 int matchdot;
6991
6992 metaflag = 0;
6993 start = name;
6994 for (p = name; *p; p++) {
6995 if (*p == '*' || *p == '?')
6996 metaflag = 1;
6997 else if (*p == '[') {
6998 char *q = p + 1;
6999 if (*q == '!')
7000 q++;
7001 for (;;) {
7002 if (*q == '\\')
7003 q++;
7004 if (*q == '/' || *q == '\0')
7005 break;
7006 if (*++q == ']') {
7007 metaflag = 1;
7008 break;
7009 }
7010 }
7011 } else if (*p == '\\')
7012 p++;
7013 else if (*p == '/') {
7014 if (metaflag)
7015 goto out;
7016 start = p + 1;
7017 }
7018 }
7019 out:
7020 if (metaflag == 0) { /* we've reached the end of the file name */
7021 if (enddir != expdir)
7022 metaflag++;
7023 p = name;
7024 do {
7025 if (*p == '\\')
7026 p++;
7027 *enddir++ = *p;
7028 } while (*p++);
7029 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7030 addfname(expdir);
7031 return;
7032 }
7033 endname = p;
7034 if (name < start) {
7035 p = name;
7036 do {
7037 if (*p == '\\')
7038 p++;
7039 *enddir++ = *p++;
7040 } while (p < start);
7041 }
7042 if (enddir == expdir) {
7043 cp = ".";
7044 } else if (enddir == expdir + 1 && *expdir == '/') {
7045 cp = "/";
7046 } else {
7047 cp = expdir;
7048 enddir[-1] = '\0';
7049 }
7050 dirp = opendir(cp);
7051 if (dirp == NULL)
7052 return;
7053 if (enddir != expdir)
7054 enddir[-1] = '/';
7055 if (*endname == 0) {
7056 atend = 1;
7057 } else {
7058 atend = 0;
7059 *endname++ = '\0';
7060 }
7061 matchdot = 0;
7062 p = start;
7063 if (*p == '\\')
7064 p++;
7065 if (*p == '.')
7066 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007067 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007068 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007069 continue;
7070 if (pmatch(start, dp->d_name)) {
7071 if (atend) {
7072 strcpy(enddir, dp->d_name);
7073 addfname(expdir);
7074 } else {
7075 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7076 continue;
7077 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007078 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007079 }
7080 }
7081 }
7082 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007083 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007084 endname[-1] = '/';
7085}
7086
7087static struct strlist *
7088msort(struct strlist *list, int len)
7089{
7090 struct strlist *p, *q = NULL;
7091 struct strlist **lpp;
7092 int half;
7093 int n;
7094
7095 if (len <= 1)
7096 return list;
7097 half = len >> 1;
7098 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007099 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007100 q = p;
7101 p = p->next;
7102 }
7103 q->next = NULL; /* terminate first half of list */
7104 q = msort(list, half); /* sort first half of list */
7105 p = msort(p, len - half); /* sort second half */
7106 lpp = &list;
7107 for (;;) {
7108#if ENABLE_LOCALE_SUPPORT
7109 if (strcoll(p->text, q->text) < 0)
7110#else
7111 if (strcmp(p->text, q->text) < 0)
7112#endif
7113 {
7114 *lpp = p;
7115 lpp = &p->next;
7116 p = *lpp;
7117 if (p == NULL) {
7118 *lpp = q;
7119 break;
7120 }
7121 } else {
7122 *lpp = q;
7123 lpp = &q->next;
7124 q = *lpp;
7125 if (q == NULL) {
7126 *lpp = p;
7127 break;
7128 }
7129 }
7130 }
7131 return list;
7132}
7133
7134/*
7135 * Sort the results of file name expansion. It calculates the number of
7136 * strings to sort and then calls msort (short for merge sort) to do the
7137 * work.
7138 */
7139static struct strlist *
7140expsort(struct strlist *str)
7141{
7142 int len;
7143 struct strlist *sp;
7144
7145 len = 0;
7146 for (sp = str; sp; sp = sp->next)
7147 len++;
7148 return msort(str, len);
7149}
7150
7151static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007152expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007153{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007154 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007155 '*', '?', '[', 0
7156 };
7157 /* TODO - EXP_REDIR */
7158
7159 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007160 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007161 struct strlist **savelastp;
7162 struct strlist *sp;
7163 char *p;
7164
7165 if (fflag)
7166 goto nometa;
7167 if (!strpbrk(str->text, metachars))
7168 goto nometa;
7169 savelastp = exparg.lastp;
7170
7171 INT_OFF;
7172 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7173 {
7174 int i = strlen(str->text);
7175 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7176 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007177 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007178 free(expdir);
7179 if (p != str->text)
7180 free(p);
7181 INT_ON;
7182 if (exparg.lastp == savelastp) {
7183 /*
7184 * no matches
7185 */
7186 nometa:
7187 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007188 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007189 exparg.lastp = &str->next;
7190 } else {
7191 *exparg.lastp = NULL;
7192 *savelastp = sp = expsort(*savelastp);
7193 while (sp->next != NULL)
7194 sp = sp->next;
7195 exparg.lastp = &sp->next;
7196 }
7197 str = str->next;
7198 }
7199}
7200
7201/*
7202 * Perform variable substitution and command substitution on an argument,
7203 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7204 * perform splitting and file name expansion. When arglist is NULL, perform
7205 * here document expansion.
7206 */
7207static void
7208expandarg(union node *arg, struct arglist *arglist, int flag)
7209{
7210 struct strlist *sp;
7211 char *p;
7212
7213 argbackq = arg->narg.backquote;
7214 STARTSTACKSTR(expdest);
7215 ifsfirst.next = NULL;
7216 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007217 argstr(arg->narg.text, flag,
7218 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007219 p = _STPUTC('\0', expdest);
7220 expdest = p - 1;
7221 if (arglist == NULL) {
7222 return; /* here document expanded */
7223 }
7224 p = grabstackstr(p);
7225 exparg.lastp = &exparg.list;
7226 /*
7227 * TODO - EXP_REDIR
7228 */
7229 if (flag & EXP_FULL) {
7230 ifsbreakup(p, &exparg);
7231 *exparg.lastp = NULL;
7232 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007233 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007234 } else {
7235 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007236 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007237 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007238 sp->text = p;
7239 *exparg.lastp = sp;
7240 exparg.lastp = &sp->next;
7241 }
7242 if (ifsfirst.next)
7243 ifsfree();
7244 *exparg.lastp = NULL;
7245 if (exparg.list) {
7246 *arglist->lastp = exparg.list;
7247 arglist->lastp = exparg.lastp;
7248 }
7249}
7250
7251/*
7252 * Expand shell variables and backquotes inside a here document.
7253 */
7254static void
7255expandhere(union node *arg, int fd)
7256{
7257 herefd = fd;
7258 expandarg(arg, (struct arglist *)NULL, 0);
7259 full_write(fd, stackblock(), expdest - (char *)stackblock());
7260}
7261
7262/*
7263 * Returns true if the pattern matches the string.
7264 */
7265static int
7266patmatch(char *pattern, const char *string)
7267{
7268 return pmatch(preglob(pattern, 0, 0), string);
7269}
7270
7271/*
7272 * See if a pattern matches in a case statement.
7273 */
7274static int
7275casematch(union node *pattern, char *val)
7276{
7277 struct stackmark smark;
7278 int result;
7279
7280 setstackmark(&smark);
7281 argbackq = pattern->narg.backquote;
7282 STARTSTACKSTR(expdest);
7283 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007284 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7285 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007286 STACKSTRNUL(expdest);
7287 result = patmatch(stackblock(), val);
7288 popstackmark(&smark);
7289 return result;
7290}
7291
7292
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007293/* ============ find_command */
7294
7295struct builtincmd {
7296 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007297 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007298 /* unsigned flags; */
7299};
7300#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007301/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007302 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007303#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007304#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007305
7306struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007307 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007308 union param {
7309 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007310 /* index >= 0 for commands without path (slashes) */
7311 /* (TODO: what exactly does the value mean? PATH position?) */
7312 /* index == -1 for commands with slashes */
7313 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007314 const struct builtincmd *cmd;
7315 struct funcnode *func;
7316 } u;
7317};
7318/* values of cmdtype */
7319#define CMDUNKNOWN -1 /* no entry in table for command */
7320#define CMDNORMAL 0 /* command is an executable program */
7321#define CMDFUNCTION 1 /* command is a shell function */
7322#define CMDBUILTIN 2 /* command is a shell builtin */
7323
7324/* action to find_command() */
7325#define DO_ERR 0x01 /* prints errors */
7326#define DO_ABS 0x02 /* checks absolute paths */
7327#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7328#define DO_ALTPATH 0x08 /* using alternate path */
7329#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7330
7331static void find_command(char *, struct cmdentry *, int, const char *);
7332
7333
7334/* ============ Hashing commands */
7335
7336/*
7337 * When commands are first encountered, they are entered in a hash table.
7338 * This ensures that a full path search will not have to be done for them
7339 * on each invocation.
7340 *
7341 * We should investigate converting to a linear search, even though that
7342 * would make the command name "hash" a misnomer.
7343 */
7344
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007345struct tblentry {
7346 struct tblentry *next; /* next entry in hash chain */
7347 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007348 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007349 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007350 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007351};
7352
Denis Vlasenko01631112007-12-16 17:20:38 +00007353static struct tblentry **cmdtable;
7354#define INIT_G_cmdtable() do { \
7355 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7356} while (0)
7357
7358static int builtinloc = -1; /* index in path of %builtin, or -1 */
7359
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007360
7361static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007362tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007363{
7364 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007365
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007366#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007367 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007368 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007369 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007370 while (*envp)
7371 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007372 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007373 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007374 /* re-exec ourselves with the new arguments */
7375 execve(bb_busybox_exec_path, argv, envp);
7376 /* If they called chroot or otherwise made the binary no longer
7377 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007378 }
7379#endif
7380
7381 repeat:
7382#ifdef SYSV
7383 do {
7384 execve(cmd, argv, envp);
7385 } while (errno == EINTR);
7386#else
7387 execve(cmd, argv, envp);
7388#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007389 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007391 return;
7392 }
7393 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007394 char **ap;
7395 char **new;
7396
7397 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007398 continue;
7399 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007400 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007401 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007402 ap += 2;
7403 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007404 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007405 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007406 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007407 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007408 goto repeat;
7409 }
7410}
7411
7412/*
7413 * Exec a program. Never returns. If you change this routine, you may
7414 * have to change the find_command routine as well.
7415 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007416static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007417static void
7418shellexec(char **argv, const char *path, int idx)
7419{
7420 char *cmdname;
7421 int e;
7422 char **envp;
7423 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007424#if ENABLE_FEATURE_SH_STANDALONE
7425 int applet_no = -1;
7426#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007427
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007428 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007429 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007430 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007431#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007432 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007433#endif
7434 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007435 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007436 e = errno;
7437 } else {
7438 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007439 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007440 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007441 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007442 if (errno != ENOENT && errno != ENOTDIR)
7443 e = errno;
7444 }
7445 stunalloc(cmdname);
7446 }
7447 }
7448
7449 /* Map to POSIX errors */
7450 switch (e) {
7451 case EACCES:
7452 exerrno = 126;
7453 break;
7454 case ENOENT:
7455 exerrno = 127;
7456 break;
7457 default:
7458 exerrno = 2;
7459 break;
7460 }
7461 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007462 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7463 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007464 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7465 /* NOTREACHED */
7466}
7467
7468static void
7469printentry(struct tblentry *cmdp)
7470{
7471 int idx;
7472 const char *path;
7473 char *name;
7474
7475 idx = cmdp->param.index;
7476 path = pathval();
7477 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007478 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007479 stunalloc(name);
7480 } while (--idx >= 0);
7481 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7482}
7483
7484/*
7485 * Clear out command entries. The argument specifies the first entry in
7486 * PATH which has changed.
7487 */
7488static void
7489clearcmdentry(int firstchange)
7490{
7491 struct tblentry **tblp;
7492 struct tblentry **pp;
7493 struct tblentry *cmdp;
7494
7495 INT_OFF;
7496 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7497 pp = tblp;
7498 while ((cmdp = *pp) != NULL) {
7499 if ((cmdp->cmdtype == CMDNORMAL &&
7500 cmdp->param.index >= firstchange)
7501 || (cmdp->cmdtype == CMDBUILTIN &&
7502 builtinloc >= firstchange)
7503 ) {
7504 *pp = cmdp->next;
7505 free(cmdp);
7506 } else {
7507 pp = &cmdp->next;
7508 }
7509 }
7510 }
7511 INT_ON;
7512}
7513
7514/*
7515 * Locate a command in the command hash table. If "add" is nonzero,
7516 * add the command to the table if it is not already present. The
7517 * variable "lastcmdentry" is set to point to the address of the link
7518 * pointing to the entry, so that delete_cmd_entry can delete the
7519 * entry.
7520 *
7521 * Interrupts must be off if called with add != 0.
7522 */
7523static struct tblentry **lastcmdentry;
7524
7525static struct tblentry *
7526cmdlookup(const char *name, int add)
7527{
7528 unsigned int hashval;
7529 const char *p;
7530 struct tblentry *cmdp;
7531 struct tblentry **pp;
7532
7533 p = name;
7534 hashval = (unsigned char)*p << 4;
7535 while (*p)
7536 hashval += (unsigned char)*p++;
7537 hashval &= 0x7FFF;
7538 pp = &cmdtable[hashval % CMDTABLESIZE];
7539 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7540 if (strcmp(cmdp->cmdname, name) == 0)
7541 break;
7542 pp = &cmdp->next;
7543 }
7544 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007545 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7546 + strlen(name)
7547 /* + 1 - already done because
7548 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007549 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007550 cmdp->cmdtype = CMDUNKNOWN;
7551 strcpy(cmdp->cmdname, name);
7552 }
7553 lastcmdentry = pp;
7554 return cmdp;
7555}
7556
7557/*
7558 * Delete the command entry returned on the last lookup.
7559 */
7560static void
7561delete_cmd_entry(void)
7562{
7563 struct tblentry *cmdp;
7564
7565 INT_OFF;
7566 cmdp = *lastcmdentry;
7567 *lastcmdentry = cmdp->next;
7568 if (cmdp->cmdtype == CMDFUNCTION)
7569 freefunc(cmdp->param.func);
7570 free(cmdp);
7571 INT_ON;
7572}
7573
7574/*
7575 * Add a new command entry, replacing any existing command entry for
7576 * the same name - except special builtins.
7577 */
7578static void
7579addcmdentry(char *name, struct cmdentry *entry)
7580{
7581 struct tblentry *cmdp;
7582
7583 cmdp = cmdlookup(name, 1);
7584 if (cmdp->cmdtype == CMDFUNCTION) {
7585 freefunc(cmdp->param.func);
7586 }
7587 cmdp->cmdtype = entry->cmdtype;
7588 cmdp->param = entry->u;
7589 cmdp->rehash = 0;
7590}
7591
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007592static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007593hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007594{
7595 struct tblentry **pp;
7596 struct tblentry *cmdp;
7597 int c;
7598 struct cmdentry entry;
7599 char *name;
7600
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007601 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007602 clearcmdentry(0);
7603 return 0;
7604 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007605
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007606 if (*argptr == NULL) {
7607 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7608 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7609 if (cmdp->cmdtype == CMDNORMAL)
7610 printentry(cmdp);
7611 }
7612 }
7613 return 0;
7614 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007615
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007616 c = 0;
7617 while ((name = *argptr) != NULL) {
7618 cmdp = cmdlookup(name, 0);
7619 if (cmdp != NULL
7620 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007621 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7622 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007623 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007624 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007625 find_command(name, &entry, DO_ERR, pathval());
7626 if (entry.cmdtype == CMDUNKNOWN)
7627 c = 1;
7628 argptr++;
7629 }
7630 return c;
7631}
7632
7633/*
7634 * Called when a cd is done. Marks all commands so the next time they
7635 * are executed they will be rehashed.
7636 */
7637static void
7638hashcd(void)
7639{
7640 struct tblentry **pp;
7641 struct tblentry *cmdp;
7642
7643 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7644 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007645 if (cmdp->cmdtype == CMDNORMAL
7646 || (cmdp->cmdtype == CMDBUILTIN
7647 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7648 && builtinloc > 0)
7649 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007650 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007651 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007652 }
7653 }
7654}
7655
7656/*
7657 * Fix command hash table when PATH changed.
7658 * Called before PATH is changed. The argument is the new value of PATH;
7659 * pathval() still returns the old value at this point.
7660 * Called with interrupts off.
7661 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007662static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007663changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007664{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007665 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007666 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007667 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007668 int idx_bltin;
7669
7670 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007671 firstchange = 9999; /* assume no change */
7672 idx = 0;
7673 idx_bltin = -1;
7674 for (;;) {
7675 if (*old != *new) {
7676 firstchange = idx;
7677 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007678 || (*old == ':' && *new == '\0')
7679 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007680 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007681 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007682 old = new; /* ignore subsequent differences */
7683 }
7684 if (*new == '\0')
7685 break;
7686 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7687 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007688 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007689 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007690 new++;
7691 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007692 }
7693 if (builtinloc < 0 && idx_bltin >= 0)
7694 builtinloc = idx_bltin; /* zap builtins */
7695 if (builtinloc >= 0 && idx_bltin < 0)
7696 firstchange = 0;
7697 clearcmdentry(firstchange);
7698 builtinloc = idx_bltin;
7699}
7700
7701#define TEOF 0
7702#define TNL 1
7703#define TREDIR 2
7704#define TWORD 3
7705#define TSEMI 4
7706#define TBACKGND 5
7707#define TAND 6
7708#define TOR 7
7709#define TPIPE 8
7710#define TLP 9
7711#define TRP 10
7712#define TENDCASE 11
7713#define TENDBQUOTE 12
7714#define TNOT 13
7715#define TCASE 14
7716#define TDO 15
7717#define TDONE 16
7718#define TELIF 17
7719#define TELSE 18
7720#define TESAC 19
7721#define TFI 20
7722#define TFOR 21
7723#define TIF 22
7724#define TIN 23
7725#define TTHEN 24
7726#define TUNTIL 25
7727#define TWHILE 26
7728#define TBEGIN 27
7729#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007730typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007731
7732/* first char is indicating which tokens mark the end of a list */
7733static const char *const tokname_array[] = {
7734 "\1end of file",
7735 "\0newline",
7736 "\0redirection",
7737 "\0word",
7738 "\0;",
7739 "\0&",
7740 "\0&&",
7741 "\0||",
7742 "\0|",
7743 "\0(",
7744 "\1)",
7745 "\1;;",
7746 "\1`",
7747#define KWDOFFSET 13
7748 /* the following are keywords */
7749 "\0!",
7750 "\0case",
7751 "\1do",
7752 "\1done",
7753 "\1elif",
7754 "\1else",
7755 "\1esac",
7756 "\1fi",
7757 "\0for",
7758 "\0if",
7759 "\0in",
7760 "\1then",
7761 "\0until",
7762 "\0while",
7763 "\0{",
7764 "\1}",
7765};
7766
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007767/* Wrapper around strcmp for qsort/bsearch/... */
7768static int
7769pstrcmp(const void *a, const void *b)
7770{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007771 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007772}
7773
7774static const char *const *
7775findkwd(const char *s)
7776{
7777 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007778 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7779 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007780}
7781
7782/*
7783 * Locate and print what a word is...
7784 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007785static int
7786describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007787{
7788 struct cmdentry entry;
7789 struct tblentry *cmdp;
7790#if ENABLE_ASH_ALIAS
7791 const struct alias *ap;
7792#endif
7793 const char *path = pathval();
7794
7795 if (describe_command_verbose) {
7796 out1str(command);
7797 }
7798
7799 /* First look at the keywords */
7800 if (findkwd(command)) {
7801 out1str(describe_command_verbose ? " is a shell keyword" : command);
7802 goto out;
7803 }
7804
7805#if ENABLE_ASH_ALIAS
7806 /* Then look at the aliases */
7807 ap = lookupalias(command, 0);
7808 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007809 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007810 out1str("alias ");
7811 printalias(ap);
7812 return 0;
7813 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007814 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007815 goto out;
7816 }
7817#endif
7818 /* Then check if it is a tracked alias */
7819 cmdp = cmdlookup(command, 0);
7820 if (cmdp != NULL) {
7821 entry.cmdtype = cmdp->cmdtype;
7822 entry.u = cmdp->param;
7823 } else {
7824 /* Finally use brute force */
7825 find_command(command, &entry, DO_ABS, path);
7826 }
7827
7828 switch (entry.cmdtype) {
7829 case CMDNORMAL: {
7830 int j = entry.u.index;
7831 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007832 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007833 p = command;
7834 } else {
7835 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007836 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007837 stunalloc(p);
7838 } while (--j >= 0);
7839 }
7840 if (describe_command_verbose) {
7841 out1fmt(" is%s %s",
7842 (cmdp ? " a tracked alias for" : nullstr), p
7843 );
7844 } else {
7845 out1str(p);
7846 }
7847 break;
7848 }
7849
7850 case CMDFUNCTION:
7851 if (describe_command_verbose) {
7852 out1str(" is a shell function");
7853 } else {
7854 out1str(command);
7855 }
7856 break;
7857
7858 case CMDBUILTIN:
7859 if (describe_command_verbose) {
7860 out1fmt(" is a %sshell builtin",
7861 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7862 "special " : nullstr
7863 );
7864 } else {
7865 out1str(command);
7866 }
7867 break;
7868
7869 default:
7870 if (describe_command_verbose) {
7871 out1str(": not found\n");
7872 }
7873 return 127;
7874 }
7875 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007876 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007877 return 0;
7878}
7879
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007880static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007881typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007882{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007883 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007884 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007885 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007886
Denis Vlasenko46846e22007-05-20 13:08:31 +00007887 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007888 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007889 i++;
7890 verbose = 0;
7891 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007892 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007893 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007894 }
7895 return err;
7896}
7897
7898#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007899static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007900commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007901{
7902 int c;
7903 enum {
7904 VERIFY_BRIEF = 1,
7905 VERIFY_VERBOSE = 2,
7906 } verify = 0;
7907
7908 while ((c = nextopt("pvV")) != '\0')
7909 if (c == 'V')
7910 verify |= VERIFY_VERBOSE;
7911 else if (c == 'v')
7912 verify |= VERIFY_BRIEF;
7913#if DEBUG
7914 else if (c != 'p')
7915 abort();
7916#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007917 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7918 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007919 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007920 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007921
7922 return 0;
7923}
7924#endif
7925
7926
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007927/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007928
Denis Vlasenko340299a2008-11-21 10:36:36 +00007929static int funcblocksize; /* size of structures in function */
7930static int funcstringsize; /* size of strings in node */
7931static void *funcblock; /* block to allocate function from */
7932static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007933
Eric Andersencb57d552001-06-28 07:25:16 +00007934/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007935#define EV_EXIT 01 /* exit after evaluating tree */
7936#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007937#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007938
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007939static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007940 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7941 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7942 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7943 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7944 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7945 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7946 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7947 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7948 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7949 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7950 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7951 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7952 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7953 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7954 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7955 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7956 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007957#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007958 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007959#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007960 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7961 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7962 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7963 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7964 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7965 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7966 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7967 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7968 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007969};
7970
7971static void calcsize(union node *n);
7972
7973static void
7974sizenodelist(struct nodelist *lp)
7975{
7976 while (lp) {
7977 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7978 calcsize(lp->n);
7979 lp = lp->next;
7980 }
7981}
7982
7983static void
7984calcsize(union node *n)
7985{
7986 if (n == NULL)
7987 return;
7988 funcblocksize += nodesize[n->type];
7989 switch (n->type) {
7990 case NCMD:
7991 calcsize(n->ncmd.redirect);
7992 calcsize(n->ncmd.args);
7993 calcsize(n->ncmd.assign);
7994 break;
7995 case NPIPE:
7996 sizenodelist(n->npipe.cmdlist);
7997 break;
7998 case NREDIR:
7999 case NBACKGND:
8000 case NSUBSHELL:
8001 calcsize(n->nredir.redirect);
8002 calcsize(n->nredir.n);
8003 break;
8004 case NAND:
8005 case NOR:
8006 case NSEMI:
8007 case NWHILE:
8008 case NUNTIL:
8009 calcsize(n->nbinary.ch2);
8010 calcsize(n->nbinary.ch1);
8011 break;
8012 case NIF:
8013 calcsize(n->nif.elsepart);
8014 calcsize(n->nif.ifpart);
8015 calcsize(n->nif.test);
8016 break;
8017 case NFOR:
8018 funcstringsize += strlen(n->nfor.var) + 1;
8019 calcsize(n->nfor.body);
8020 calcsize(n->nfor.args);
8021 break;
8022 case NCASE:
8023 calcsize(n->ncase.cases);
8024 calcsize(n->ncase.expr);
8025 break;
8026 case NCLIST:
8027 calcsize(n->nclist.body);
8028 calcsize(n->nclist.pattern);
8029 calcsize(n->nclist.next);
8030 break;
8031 case NDEFUN:
8032 case NARG:
8033 sizenodelist(n->narg.backquote);
8034 funcstringsize += strlen(n->narg.text) + 1;
8035 calcsize(n->narg.next);
8036 break;
8037 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008038#if ENABLE_ASH_BASH_COMPAT
8039 case NTO2:
8040#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008041 case NCLOBBER:
8042 case NFROM:
8043 case NFROMTO:
8044 case NAPPEND:
8045 calcsize(n->nfile.fname);
8046 calcsize(n->nfile.next);
8047 break;
8048 case NTOFD:
8049 case NFROMFD:
8050 calcsize(n->ndup.vname);
8051 calcsize(n->ndup.next);
8052 break;
8053 case NHERE:
8054 case NXHERE:
8055 calcsize(n->nhere.doc);
8056 calcsize(n->nhere.next);
8057 break;
8058 case NNOT:
8059 calcsize(n->nnot.com);
8060 break;
8061 };
8062}
8063
8064static char *
8065nodeckstrdup(char *s)
8066{
8067 char *rtn = funcstring;
8068
8069 strcpy(funcstring, s);
8070 funcstring += strlen(s) + 1;
8071 return rtn;
8072}
8073
8074static union node *copynode(union node *);
8075
8076static struct nodelist *
8077copynodelist(struct nodelist *lp)
8078{
8079 struct nodelist *start;
8080 struct nodelist **lpp;
8081
8082 lpp = &start;
8083 while (lp) {
8084 *lpp = funcblock;
8085 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8086 (*lpp)->n = copynode(lp->n);
8087 lp = lp->next;
8088 lpp = &(*lpp)->next;
8089 }
8090 *lpp = NULL;
8091 return start;
8092}
8093
8094static union node *
8095copynode(union node *n)
8096{
8097 union node *new;
8098
8099 if (n == NULL)
8100 return NULL;
8101 new = funcblock;
8102 funcblock = (char *) funcblock + nodesize[n->type];
8103
8104 switch (n->type) {
8105 case NCMD:
8106 new->ncmd.redirect = copynode(n->ncmd.redirect);
8107 new->ncmd.args = copynode(n->ncmd.args);
8108 new->ncmd.assign = copynode(n->ncmd.assign);
8109 break;
8110 case NPIPE:
8111 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008112 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008113 break;
8114 case NREDIR:
8115 case NBACKGND:
8116 case NSUBSHELL:
8117 new->nredir.redirect = copynode(n->nredir.redirect);
8118 new->nredir.n = copynode(n->nredir.n);
8119 break;
8120 case NAND:
8121 case NOR:
8122 case NSEMI:
8123 case NWHILE:
8124 case NUNTIL:
8125 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8126 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8127 break;
8128 case NIF:
8129 new->nif.elsepart = copynode(n->nif.elsepart);
8130 new->nif.ifpart = copynode(n->nif.ifpart);
8131 new->nif.test = copynode(n->nif.test);
8132 break;
8133 case NFOR:
8134 new->nfor.var = nodeckstrdup(n->nfor.var);
8135 new->nfor.body = copynode(n->nfor.body);
8136 new->nfor.args = copynode(n->nfor.args);
8137 break;
8138 case NCASE:
8139 new->ncase.cases = copynode(n->ncase.cases);
8140 new->ncase.expr = copynode(n->ncase.expr);
8141 break;
8142 case NCLIST:
8143 new->nclist.body = copynode(n->nclist.body);
8144 new->nclist.pattern = copynode(n->nclist.pattern);
8145 new->nclist.next = copynode(n->nclist.next);
8146 break;
8147 case NDEFUN:
8148 case NARG:
8149 new->narg.backquote = copynodelist(n->narg.backquote);
8150 new->narg.text = nodeckstrdup(n->narg.text);
8151 new->narg.next = copynode(n->narg.next);
8152 break;
8153 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008154#if ENABLE_ASH_BASH_COMPAT
8155 case NTO2:
8156#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008157 case NCLOBBER:
8158 case NFROM:
8159 case NFROMTO:
8160 case NAPPEND:
8161 new->nfile.fname = copynode(n->nfile.fname);
8162 new->nfile.fd = n->nfile.fd;
8163 new->nfile.next = copynode(n->nfile.next);
8164 break;
8165 case NTOFD:
8166 case NFROMFD:
8167 new->ndup.vname = copynode(n->ndup.vname);
8168 new->ndup.dupfd = n->ndup.dupfd;
8169 new->ndup.fd = n->ndup.fd;
8170 new->ndup.next = copynode(n->ndup.next);
8171 break;
8172 case NHERE:
8173 case NXHERE:
8174 new->nhere.doc = copynode(n->nhere.doc);
8175 new->nhere.fd = n->nhere.fd;
8176 new->nhere.next = copynode(n->nhere.next);
8177 break;
8178 case NNOT:
8179 new->nnot.com = copynode(n->nnot.com);
8180 break;
8181 };
8182 new->type = n->type;
8183 return new;
8184}
8185
8186/*
8187 * Make a copy of a parse tree.
8188 */
8189static struct funcnode *
8190copyfunc(union node *n)
8191{
8192 struct funcnode *f;
8193 size_t blocksize;
8194
8195 funcblocksize = offsetof(struct funcnode, n);
8196 funcstringsize = 0;
8197 calcsize(n);
8198 blocksize = funcblocksize;
8199 f = ckmalloc(blocksize + funcstringsize);
8200 funcblock = (char *) f + offsetof(struct funcnode, n);
8201 funcstring = (char *) f + blocksize;
8202 copynode(n);
8203 f->count = 0;
8204 return f;
8205}
8206
8207/*
8208 * Define a shell function.
8209 */
8210static void
8211defun(char *name, union node *func)
8212{
8213 struct cmdentry entry;
8214
8215 INT_OFF;
8216 entry.cmdtype = CMDFUNCTION;
8217 entry.u.func = copyfunc(func);
8218 addcmdentry(name, &entry);
8219 INT_ON;
8220}
8221
Denis Vlasenko4b875702009-03-19 13:30:04 +00008222/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008223#define SKIPBREAK (1 << 0)
8224#define SKIPCONT (1 << 1)
8225#define SKIPFUNC (1 << 2)
8226#define SKIPFILE (1 << 3)
8227#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008228static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008229static int skipcount; /* number of levels to skip */
8230static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008231static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008232
Denis Vlasenko4b875702009-03-19 13:30:04 +00008233/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008234static int evalstring(char *s, int mask);
8235
Denis Vlasenko4b875702009-03-19 13:30:04 +00008236/* Called to execute a trap.
8237 * Single callsite - at the end of evaltree().
8238 * If we return non-zero, exaltree raises EXEXIT exception.
8239 *
8240 * Perhaps we should avoid entering new trap handlers
8241 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008242 */
8243static int
8244dotrap(void)
8245{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008246 uint8_t *g;
8247 int sig;
8248 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008249
8250 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008251 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008252 xbarrier();
8253
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008254 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008255 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8256 int want_exexit;
8257 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008258
Denis Vlasenko4b875702009-03-19 13:30:04 +00008259 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008260 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008261 t = trap[sig];
8262 /* non-trapped SIGINT is handled separately by raise_interrupt,
8263 * don't upset it by resetting gotsig[SIGINT-1] */
8264 if (sig == SIGINT && !t)
8265 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008266
8267 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008268 *g = 0;
8269 if (!t)
8270 continue;
8271 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008272 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008273 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008274 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008275 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008276 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008277 }
8278
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008279 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008280 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008281}
8282
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008283/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008284static void evalloop(union node *, int);
8285static void evalfor(union node *, int);
8286static void evalcase(union node *, int);
8287static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008288static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008289static void evalpipe(union node *, int);
8290static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008291static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008292static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008293
Eric Andersen62483552001-07-10 06:09:16 +00008294/*
Eric Andersenc470f442003-07-28 09:56:35 +00008295 * Evaluate a parse tree. The value is left in the global variable
8296 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008297 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008298static void
Eric Andersenc470f442003-07-28 09:56:35 +00008299evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008300{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008301 struct jmploc *volatile savehandler = exception_handler;
8302 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008303 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008304 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008305 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008306 int int_level;
8307
8308 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008309
Eric Andersenc470f442003-07-28 09:56:35 +00008310 if (n == NULL) {
8311 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008312 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008313 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008314 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008315
8316 exception_handler = &jmploc;
8317 {
8318 int err = setjmp(jmploc.loc);
8319 if (err) {
8320 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008321 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008322 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8323 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008324 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008325 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008326 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008327 TRACE(("exception %d in evaltree, propagating err=%d\n",
8328 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008329 exception_handler = savehandler;
8330 longjmp(exception_handler->loc, err);
8331 }
8332 }
8333
Eric Andersenc470f442003-07-28 09:56:35 +00008334 switch (n->type) {
8335 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008336#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008337 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008338 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008339 break;
8340#endif
8341 case NNOT:
8342 evaltree(n->nnot.com, EV_TESTED);
8343 status = !exitstatus;
8344 goto setstatus;
8345 case NREDIR:
8346 expredir(n->nredir.redirect);
8347 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8348 if (!status) {
8349 evaltree(n->nredir.n, flags & EV_TESTED);
8350 status = exitstatus;
8351 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008352 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008353 goto setstatus;
8354 case NCMD:
8355 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008356 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008357 if (eflag && !(flags & EV_TESTED))
8358 checkexit = ~0;
8359 goto calleval;
8360 case NFOR:
8361 evalfn = evalfor;
8362 goto calleval;
8363 case NWHILE:
8364 case NUNTIL:
8365 evalfn = evalloop;
8366 goto calleval;
8367 case NSUBSHELL:
8368 case NBACKGND:
8369 evalfn = evalsubshell;
8370 goto calleval;
8371 case NPIPE:
8372 evalfn = evalpipe;
8373 goto checkexit;
8374 case NCASE:
8375 evalfn = evalcase;
8376 goto calleval;
8377 case NAND:
8378 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008379 case NSEMI: {
8380
Eric Andersenc470f442003-07-28 09:56:35 +00008381#if NAND + 1 != NOR
8382#error NAND + 1 != NOR
8383#endif
8384#if NOR + 1 != NSEMI
8385#error NOR + 1 != NSEMI
8386#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008387 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008388 evaltree(
8389 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008390 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008391 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008392 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008393 break;
8394 if (!evalskip) {
8395 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008396 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008397 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008398 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008399 evalfn(n, flags);
8400 break;
8401 }
8402 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008403 }
Eric Andersenc470f442003-07-28 09:56:35 +00008404 case NIF:
8405 evaltree(n->nif.test, EV_TESTED);
8406 if (evalskip)
8407 break;
8408 if (exitstatus == 0) {
8409 n = n->nif.ifpart;
8410 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008411 }
8412 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008413 n = n->nif.elsepart;
8414 goto evaln;
8415 }
8416 goto success;
8417 case NDEFUN:
8418 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008419 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008420 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008421 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008422 exitstatus = status;
8423 break;
8424 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008425
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008426 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008427 exception_handler = savehandler;
8428 out1:
8429 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008430 evalskip |= SKIPEVAL;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008431 else if (pending_sig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008432 goto exexit;
8433
8434 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008435 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008436 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008437 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008438
8439 RESTORE_INT(int_level);
8440 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008441}
8442
Eric Andersenc470f442003-07-28 09:56:35 +00008443#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8444static
8445#endif
8446void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8447
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008448static void
Eric Andersenc470f442003-07-28 09:56:35 +00008449evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008450{
8451 int status;
8452
8453 loopnest++;
8454 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008455 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008456 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008457 int i;
8458
Eric Andersencb57d552001-06-28 07:25:16 +00008459 evaltree(n->nbinary.ch1, EV_TESTED);
8460 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008461 skipping:
8462 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008463 evalskip = 0;
8464 continue;
8465 }
8466 if (evalskip == SKIPBREAK && --skipcount <= 0)
8467 evalskip = 0;
8468 break;
8469 }
Eric Andersenc470f442003-07-28 09:56:35 +00008470 i = exitstatus;
8471 if (n->type != NWHILE)
8472 i = !i;
8473 if (i != 0)
8474 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008475 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008476 status = exitstatus;
8477 if (evalskip)
8478 goto skipping;
8479 }
8480 loopnest--;
8481 exitstatus = status;
8482}
8483
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008484static void
Eric Andersenc470f442003-07-28 09:56:35 +00008485evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008486{
8487 struct arglist arglist;
8488 union node *argp;
8489 struct strlist *sp;
8490 struct stackmark smark;
8491
8492 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008493 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008494 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008495 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008496 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008497 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008498 if (evalskip)
8499 goto out;
8500 }
8501 *arglist.lastp = NULL;
8502
8503 exitstatus = 0;
8504 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008505 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008506 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008507 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008508 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008509 if (evalskip) {
8510 if (evalskip == SKIPCONT && --skipcount <= 0) {
8511 evalskip = 0;
8512 continue;
8513 }
8514 if (evalskip == SKIPBREAK && --skipcount <= 0)
8515 evalskip = 0;
8516 break;
8517 }
8518 }
8519 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008520 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008521 popstackmark(&smark);
8522}
8523
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008524static void
Eric Andersenc470f442003-07-28 09:56:35 +00008525evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008526{
8527 union node *cp;
8528 union node *patp;
8529 struct arglist arglist;
8530 struct stackmark smark;
8531
8532 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008533 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008534 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008535 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008536 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008537 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8538 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008539 if (casematch(patp, arglist.list->text)) {
8540 if (evalskip == 0) {
8541 evaltree(cp->nclist.body, flags);
8542 }
8543 goto out;
8544 }
8545 }
8546 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008547 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008548 popstackmark(&smark);
8549}
8550
Eric Andersenc470f442003-07-28 09:56:35 +00008551/*
8552 * Kick off a subshell to evaluate a tree.
8553 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008554static void
Eric Andersenc470f442003-07-28 09:56:35 +00008555evalsubshell(union node *n, int flags)
8556{
8557 struct job *jp;
8558 int backgnd = (n->type == NBACKGND);
8559 int status;
8560
8561 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008562 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008563 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008564 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008565 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008566 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008567 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008568 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008569 flags |= EV_EXIT;
8570 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008571 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008572 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008573 redirect(n->nredir.redirect, 0);
8574 evaltreenr(n->nredir.n, flags);
8575 /* never returns */
8576 }
8577 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008578 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008579 status = waitforjob(jp);
8580 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008581 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008582}
8583
Eric Andersenc470f442003-07-28 09:56:35 +00008584/*
8585 * Compute the names of the files in a redirection list.
8586 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008587static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008588static void
8589expredir(union node *n)
8590{
8591 union node *redir;
8592
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008593 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008594 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008595
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008596 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008597 fn.lastp = &fn.list;
8598 switch (redir->type) {
8599 case NFROMTO:
8600 case NFROM:
8601 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008602#if ENABLE_ASH_BASH_COMPAT
8603 case NTO2:
8604#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008605 case NCLOBBER:
8606 case NAPPEND:
8607 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008608#if ENABLE_ASH_BASH_COMPAT
8609 store_expfname:
8610#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008611 redir->nfile.expfname = fn.list->text;
8612 break;
8613 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008614 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008615 if (redir->ndup.vname) {
8616 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008617 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008618 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008619#if ENABLE_ASH_BASH_COMPAT
8620//FIXME: we used expandarg with different args!
8621 if (!isdigit_str9(fn.list->text)) {
8622 /* >&file, not >&fd */
8623 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8624 ash_msg_and_raise_error("redir error");
8625 redir->type = NTO2;
8626 goto store_expfname;
8627 }
8628#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008629 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008630 }
8631 break;
8632 }
8633 }
8634}
8635
Eric Andersencb57d552001-06-28 07:25:16 +00008636/*
Eric Andersencb57d552001-06-28 07:25:16 +00008637 * Evaluate a pipeline. All the processes in the pipeline are children
8638 * of the process creating the pipeline. (This differs from some versions
8639 * of the shell, which make the last process in a pipeline the parent
8640 * of all the rest.)
8641 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008642static void
Eric Andersenc470f442003-07-28 09:56:35 +00008643evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008644{
8645 struct job *jp;
8646 struct nodelist *lp;
8647 int pipelen;
8648 int prevfd;
8649 int pip[2];
8650
Eric Andersenc470f442003-07-28 09:56:35 +00008651 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008652 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008653 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008654 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008655 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008656 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008657 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008658 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008659 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008660 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008661 pip[1] = -1;
8662 if (lp->next) {
8663 if (pipe(pip) < 0) {
8664 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008665 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008666 }
8667 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008668 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008669 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008670 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008671 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008672 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008673 if (prevfd > 0) {
8674 dup2(prevfd, 0);
8675 close(prevfd);
8676 }
8677 if (pip[1] > 1) {
8678 dup2(pip[1], 1);
8679 close(pip[1]);
8680 }
Eric Andersenc470f442003-07-28 09:56:35 +00008681 evaltreenr(lp->n, flags);
8682 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008683 }
8684 if (prevfd >= 0)
8685 close(prevfd);
8686 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008687 /* Don't want to trigger debugging */
8688 if (pip[1] != -1)
8689 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008690 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008691 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008692 exitstatus = waitforjob(jp);
8693 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008694 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008695 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008696}
8697
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008698/*
8699 * Controls whether the shell is interactive or not.
8700 */
8701static void
8702setinteractive(int on)
8703{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008704 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008705
8706 if (++on == is_interactive)
8707 return;
8708 is_interactive = on;
8709 setsignal(SIGINT);
8710 setsignal(SIGQUIT);
8711 setsignal(SIGTERM);
8712#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8713 if (is_interactive > 1) {
8714 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008715 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008716
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008717 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008718 /* note: ash and hush share this string */
8719 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008720 "Enter 'help' for a list of built-in commands."
8721 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008722 bb_banner,
8723 "built-in shell (ash)"
8724 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008725 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008726 }
8727 }
8728#endif
8729}
8730
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008731static void
8732optschanged(void)
8733{
8734#if DEBUG
8735 opentrace();
8736#endif
8737 setinteractive(iflag);
8738 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008739#if ENABLE_FEATURE_EDITING_VI
8740 if (viflag)
8741 line_input_state->flags |= VI_MODE;
8742 else
8743 line_input_state->flags &= ~VI_MODE;
8744#else
8745 viflag = 0; /* forcibly keep the option off */
8746#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008747}
8748
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008749static struct localvar *localvars;
8750
8751/*
8752 * Called after a function returns.
8753 * Interrupts must be off.
8754 */
8755static void
8756poplocalvars(void)
8757{
8758 struct localvar *lvp;
8759 struct var *vp;
8760
8761 while ((lvp = localvars) != NULL) {
8762 localvars = lvp->next;
8763 vp = lvp->vp;
Denys Vlasenko883cea42009-07-11 15:31:59 +02008764 TRACE(("poplocalvar %s\n", vp ? vp->text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008765 if (vp == NULL) { /* $- saved */
8766 memcpy(optlist, lvp->text, sizeof(optlist));
8767 free((char*)lvp->text);
8768 optschanged();
8769 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008770 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008771 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008772 if (vp->var_func)
8773 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008774 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008775 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008776 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008777 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008778 }
8779 free(lvp);
8780 }
8781}
8782
8783static int
8784evalfun(struct funcnode *func, int argc, char **argv, int flags)
8785{
8786 volatile struct shparam saveparam;
8787 struct localvar *volatile savelocalvars;
8788 struct jmploc *volatile savehandler;
8789 struct jmploc jmploc;
8790 int e;
8791
8792 saveparam = shellparam;
8793 savelocalvars = localvars;
8794 e = setjmp(jmploc.loc);
8795 if (e) {
8796 goto funcdone;
8797 }
8798 INT_OFF;
8799 savehandler = exception_handler;
8800 exception_handler = &jmploc;
8801 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008802 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008803 func->count++;
8804 funcnest++;
8805 INT_ON;
8806 shellparam.nparam = argc - 1;
8807 shellparam.p = argv + 1;
8808#if ENABLE_ASH_GETOPTS
8809 shellparam.optind = 1;
8810 shellparam.optoff = -1;
8811#endif
8812 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008813 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008814 INT_OFF;
8815 funcnest--;
8816 freefunc(func);
8817 poplocalvars();
8818 localvars = savelocalvars;
8819 freeparam(&shellparam);
8820 shellparam = saveparam;
8821 exception_handler = savehandler;
8822 INT_ON;
8823 evalskip &= ~SKIPFUNC;
8824 return e;
8825}
8826
Denis Vlasenko131ae172007-02-18 13:00:19 +00008827#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008828static char **
8829parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008830{
8831 char *cp, c;
8832
8833 for (;;) {
8834 cp = *++argv;
8835 if (!cp)
8836 return 0;
8837 if (*cp++ != '-')
8838 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008839 c = *cp++;
8840 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008841 break;
8842 if (c == '-' && !*cp) {
8843 argv++;
8844 break;
8845 }
8846 do {
8847 switch (c) {
8848 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008849 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008850 break;
8851 default:
8852 /* run 'typecmd' for other options */
8853 return 0;
8854 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008855 c = *cp++;
8856 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008857 }
8858 return argv;
8859}
8860#endif
8861
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008862/*
8863 * Make a variable a local variable. When a variable is made local, it's
8864 * value and flags are saved in a localvar structure. The saved values
8865 * will be restored when the shell function returns. We handle the name
8866 * "-" as a special case.
8867 */
8868static void
8869mklocal(char *name)
8870{
8871 struct localvar *lvp;
8872 struct var **vpp;
8873 struct var *vp;
8874
8875 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008876 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008877 if (LONE_DASH(name)) {
8878 char *p;
8879 p = ckmalloc(sizeof(optlist));
8880 lvp->text = memcpy(p, optlist, sizeof(optlist));
8881 vp = NULL;
8882 } else {
8883 char *eq;
8884
8885 vpp = hashvar(name);
8886 vp = *findvar(vpp, name);
8887 eq = strchr(name, '=');
8888 if (vp == NULL) {
8889 if (eq)
8890 setvareq(name, VSTRFIXED);
8891 else
8892 setvar(name, NULL, VSTRFIXED);
8893 vp = *vpp; /* the new variable */
8894 lvp->flags = VUNSET;
8895 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008896 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008897 lvp->flags = vp->flags;
8898 vp->flags |= VSTRFIXED|VTEXTFIXED;
8899 if (eq)
8900 setvareq(name, 0);
8901 }
8902 }
8903 lvp->vp = vp;
8904 lvp->next = localvars;
8905 localvars = lvp;
8906 INT_ON;
8907}
8908
8909/*
8910 * The "local" command.
8911 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008912static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008913localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008914{
8915 char *name;
8916
8917 argv = argptr;
8918 while ((name = *argv++) != NULL) {
8919 mklocal(name);
8920 }
8921 return 0;
8922}
8923
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008924static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008925falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008926{
8927 return 1;
8928}
8929
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008930static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008931truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008932{
8933 return 0;
8934}
8935
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008936static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008937execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008938{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008939 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008940 iflag = 0; /* exit on error */
8941 mflag = 0;
8942 optschanged();
8943 shellexec(argv + 1, pathval(), 0);
8944 }
8945 return 0;
8946}
8947
8948/*
8949 * The return command.
8950 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008951static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008952returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008953{
8954 /*
8955 * If called outside a function, do what ksh does;
8956 * skip the rest of the file.
8957 */
8958 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8959 return argv[1] ? number(argv[1]) : exitstatus;
8960}
8961
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008962/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008963static int breakcmd(int, char **) FAST_FUNC;
8964static int dotcmd(int, char **) FAST_FUNC;
8965static int evalcmd(int, char **) FAST_FUNC;
8966static int exitcmd(int, char **) FAST_FUNC;
8967static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008968#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008969static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008970#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008971#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008972static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00008973#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00008974#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008975static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008976#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008977static int readcmd(int, char **) FAST_FUNC;
8978static int setcmd(int, char **) FAST_FUNC;
8979static int shiftcmd(int, char **) FAST_FUNC;
8980static int timescmd(int, char **) FAST_FUNC;
8981static int trapcmd(int, char **) FAST_FUNC;
8982static int umaskcmd(int, char **) FAST_FUNC;
8983static int unsetcmd(int, char **) FAST_FUNC;
8984static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008985
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008986#define BUILTIN_NOSPEC "0"
8987#define BUILTIN_SPECIAL "1"
8988#define BUILTIN_REGULAR "2"
8989#define BUILTIN_SPEC_REG "3"
8990#define BUILTIN_ASSIGN "4"
8991#define BUILTIN_SPEC_ASSG "5"
8992#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008993#define BUILTIN_SPEC_REG_ASSG "7"
8994
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008995/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008996#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008997static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008998#endif
8999#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009000static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009001#endif
9002#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009003static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009004#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009005
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009006/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009007static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009008 { BUILTIN_SPEC_REG "." , dotcmd },
9009 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009010#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009011 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009012#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009013 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009014#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009015#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009016#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009017 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009018#endif
9019#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009020 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009021#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009022 { BUILTIN_SPEC_REG "break" , breakcmd },
9023 { BUILTIN_REGULAR "cd" , cdcmd },
9024 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009025#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009026 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009027#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009028 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009029#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009030 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009031#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009032 { BUILTIN_SPEC_REG "eval" , evalcmd },
9033 { BUILTIN_SPEC_REG "exec" , execcmd },
9034 { BUILTIN_SPEC_REG "exit" , exitcmd },
9035 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9036 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009037#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009038 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009039#endif
9040#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009041 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009042#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009043 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009044#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009045 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009046#endif
9047#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009048 { BUILTIN_REGULAR "jobs" , jobscmd },
9049 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009050#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009051#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009052 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009053#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009054 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009055#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009056 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009057#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009058 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9059 { BUILTIN_REGULAR "read" , readcmd },
9060 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9061 { BUILTIN_SPEC_REG "return" , returncmd },
9062 { BUILTIN_SPEC_REG "set" , setcmd },
9063 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009064#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009065 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009066#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009067#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009068 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009069#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009070 { BUILTIN_SPEC_REG "times" , timescmd },
9071 { BUILTIN_SPEC_REG "trap" , trapcmd },
9072 { BUILTIN_REGULAR "true" , truecmd },
9073 { BUILTIN_NOSPEC "type" , typecmd },
9074 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9075 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009076#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009077 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009078#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009079 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9080 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009081};
9082
Denis Vlasenko80591b02008-03-25 07:49:43 +00009083/* Should match the above table! */
9084#define COMMANDCMD (builtintab + \
9085 2 + \
9086 1 * ENABLE_ASH_BUILTIN_TEST + \
9087 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9088 1 * ENABLE_ASH_ALIAS + \
9089 1 * ENABLE_ASH_JOB_CONTROL + \
9090 3)
9091#define EXECCMD (builtintab + \
9092 2 + \
9093 1 * ENABLE_ASH_BUILTIN_TEST + \
9094 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9095 1 * ENABLE_ASH_ALIAS + \
9096 1 * ENABLE_ASH_JOB_CONTROL + \
9097 3 + \
9098 1 * ENABLE_ASH_CMDCMD + \
9099 1 + \
9100 ENABLE_ASH_BUILTIN_ECHO + \
9101 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009102
9103/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009104 * Search the table of builtin commands.
9105 */
9106static struct builtincmd *
9107find_builtin(const char *name)
9108{
9109 struct builtincmd *bp;
9110
9111 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009112 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009113 pstrcmp
9114 );
9115 return bp;
9116}
9117
9118/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009119 * Execute a simple command.
9120 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009121static int
9122isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009123{
9124 const char *q = endofname(p);
9125 if (p == q)
9126 return 0;
9127 return *q == '=';
9128}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009129static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009130bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009131{
9132 /* Preserve exitstatus of a previous possible redirection
9133 * as POSIX mandates */
9134 return back_exitstatus;
9135}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009136static void
Eric Andersenc470f442003-07-28 09:56:35 +00009137evalcommand(union node *cmd, int flags)
9138{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009139 static const struct builtincmd null_bltin = {
9140 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009141 };
Eric Andersenc470f442003-07-28 09:56:35 +00009142 struct stackmark smark;
9143 union node *argp;
9144 struct arglist arglist;
9145 struct arglist varlist;
9146 char **argv;
9147 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009148 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009149 struct cmdentry cmdentry;
9150 struct job *jp;
9151 char *lastarg;
9152 const char *path;
9153 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009154 int status;
9155 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009156 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009157 smallint cmd_is_exec;
9158 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009159
9160 /* First expand the arguments. */
9161 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9162 setstackmark(&smark);
9163 back_exitstatus = 0;
9164
9165 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009166 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009167 varlist.lastp = &varlist.list;
9168 *varlist.lastp = NULL;
9169 arglist.lastp = &arglist.list;
9170 *arglist.lastp = NULL;
9171
9172 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009173 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009174 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9175 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9176 }
9177
Eric Andersenc470f442003-07-28 09:56:35 +00009178 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9179 struct strlist **spp;
9180
9181 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009182 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009183 expandarg(argp, &arglist, EXP_VARTILDE);
9184 else
9185 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9186
Eric Andersenc470f442003-07-28 09:56:35 +00009187 for (sp = *spp; sp; sp = sp->next)
9188 argc++;
9189 }
9190
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009191 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009192 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009193 TRACE(("evalcommand arg: %s\n", sp->text));
9194 *nargv++ = sp->text;
9195 }
9196 *nargv = NULL;
9197
9198 lastarg = NULL;
9199 if (iflag && funcnest == 0 && argc > 0)
9200 lastarg = nargv[-1];
9201
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009202 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009203 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009204 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009205
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009206 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009207 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9208 struct strlist **spp;
9209 char *p;
9210
9211 spp = varlist.lastp;
9212 expandarg(argp, &varlist, EXP_VARTILDE);
9213
9214 /*
9215 * Modify the command lookup path, if a PATH= assignment
9216 * is present
9217 */
9218 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009219 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009220 path = p;
9221 }
9222
9223 /* Print the command if xflag is set. */
9224 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009225 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009226 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009227
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009228 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009229 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009230 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009231 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009232 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009233 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009234 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009235 }
9236 sp = arglist.list;
9237 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009238 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009239 }
9240
9241 cmd_is_exec = 0;
9242 spclbltin = -1;
9243
9244 /* Now locate the command. */
9245 if (argc) {
9246 const char *oldpath;
9247 int cmd_flag = DO_ERR;
9248
9249 path += 5;
9250 oldpath = path;
9251 for (;;) {
9252 find_command(argv[0], &cmdentry, cmd_flag, path);
9253 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009254 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009255 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009256 goto bail;
9257 }
9258
9259 /* implement bltin and command here */
9260 if (cmdentry.cmdtype != CMDBUILTIN)
9261 break;
9262 if (spclbltin < 0)
9263 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9264 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009265 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009266#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009267 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009268 path = oldpath;
9269 nargv = parse_command_args(argv, &path);
9270 if (!nargv)
9271 break;
9272 argc -= nargv - argv;
9273 argv = nargv;
9274 cmd_flag |= DO_NOFUNC;
9275 } else
9276#endif
9277 break;
9278 }
9279 }
9280
9281 if (status) {
9282 /* We have a redirection error. */
9283 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009284 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009285 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009286 exitstatus = status;
9287 goto out;
9288 }
9289
9290 /* Execute the command. */
9291 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009292 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009293
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009294#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009295/* (1) BUG: if variables are set, we need to fork, or save/restore them
9296 * around run_nofork_applet() call.
9297 * (2) Should this check also be done in forkshell()?
9298 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9299 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009300 /* find_command() encodes applet_no as (-2 - applet_no) */
9301 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009302 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009303 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009304 /* run <applet>_main() */
9305 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009306 break;
9307 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009308#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009309 /* Can we avoid forking off? For example, very last command
9310 * in a script or a subshell does not need forking,
9311 * we can just exec it.
9312 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009313 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009314 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009315 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009316 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009317 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009318 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009319 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009320 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009321 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009322 break;
9323 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009324 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009325 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009326 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009327 }
9328 listsetvar(varlist.list, VEXPORT|VSTACK);
9329 shellexec(argv, path, cmdentry.u.index);
9330 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009331 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009332 case CMDBUILTIN:
9333 cmdenviron = varlist.list;
9334 if (cmdenviron) {
9335 struct strlist *list = cmdenviron;
9336 int i = VNOSET;
9337 if (spclbltin > 0 || argc == 0) {
9338 i = 0;
9339 if (cmd_is_exec && argc > 1)
9340 i = VEXPORT;
9341 }
9342 listsetvar(list, i);
9343 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009344 /* Tight loop with builtins only:
9345 * "while kill -0 $child; do true; done"
9346 * will never exit even if $child died, unless we do this
9347 * to reap the zombie and make kill detect that it's gone: */
9348 dowait(DOWAIT_NONBLOCK, NULL);
9349
Eric Andersenc470f442003-07-28 09:56:35 +00009350 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9351 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009352 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009353 if (i == EXEXIT)
9354 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009355 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009356 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009357 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009358 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009359 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009360 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009361 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009362 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009363 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009364 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009365 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009366 }
9367 break;
9368
9369 case CMDFUNCTION:
9370 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009371 /* See above for the rationale */
9372 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009373 if (evalfun(cmdentry.u.func, argc, argv, flags))
9374 goto raise;
9375 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009376
9377 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009378
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009379 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009380 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009381 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009382 /* dsl: I think this is intended to be used to support
9383 * '_' in 'vi' command mode during line editing...
9384 * However I implemented that within libedit itself.
9385 */
9386 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009387 }
Eric Andersenc470f442003-07-28 09:56:35 +00009388 popstackmark(&smark);
9389}
9390
9391static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009392evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9393{
Eric Andersenc470f442003-07-28 09:56:35 +00009394 char *volatile savecmdname;
9395 struct jmploc *volatile savehandler;
9396 struct jmploc jmploc;
9397 int i;
9398
9399 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009400 i = setjmp(jmploc.loc);
9401 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009402 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009403 savehandler = exception_handler;
9404 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009405 commandname = argv[0];
9406 argptr = argv + 1;
9407 optptr = NULL; /* initialize nextopt */
9408 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009409 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009410 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009411 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009412 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009413 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009414 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009415
9416 return i;
9417}
9418
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009419static int
9420goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009421{
9422 return !*endofname(p);
9423}
9424
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009425
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009426/*
9427 * Search for a command. This is called before we fork so that the
9428 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009429 * the child. The check for "goodname" is an overly conservative
9430 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009431 */
Eric Andersenc470f442003-07-28 09:56:35 +00009432static void
9433prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009434{
9435 struct cmdentry entry;
9436
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009437 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9438 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009439}
9440
Eric Andersencb57d552001-06-28 07:25:16 +00009441
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009442/* ============ Builtin commands
9443 *
9444 * Builtin commands whose functions are closely tied to evaluation
9445 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009446 */
9447
9448/*
Eric Andersencb57d552001-06-28 07:25:16 +00009449 * Handle break and continue commands. Break, continue, and return are
9450 * all handled by setting the evalskip flag. The evaluation routines
9451 * above all check this flag, and if it is set they start skipping
9452 * commands rather than executing them. The variable skipcount is
9453 * the number of loops to break/continue, or the number of function
9454 * levels to return. (The latter is always 1.) It should probably
9455 * be an error to break out of more loops than exist, but it isn't
9456 * in the standard shell so we don't make it one here.
9457 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009458static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009459breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009460{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009461 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009462
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009463 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009464 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009465 if (n > loopnest)
9466 n = loopnest;
9467 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009468 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009469 skipcount = n;
9470 }
9471 return 0;
9472}
9473
Eric Andersenc470f442003-07-28 09:56:35 +00009474
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009475/* ============ input.c
9476 *
Eric Andersen90898442003-08-06 11:20:52 +00009477 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009478 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009479
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009480enum {
9481 INPUT_PUSH_FILE = 1,
9482 INPUT_NOFILE_OK = 2,
9483};
Eric Andersencb57d552001-06-28 07:25:16 +00009484
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009485static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009486/* values of checkkwd variable */
9487#define CHKALIAS 0x1
9488#define CHKKWD 0x2
9489#define CHKNL 0x4
9490
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009491/*
9492 * Push a string back onto the input at this current parsefile level.
9493 * We handle aliases this way.
9494 */
9495#if !ENABLE_ASH_ALIAS
9496#define pushstring(s, ap) pushstring(s)
9497#endif
9498static void
9499pushstring(char *s, struct alias *ap)
9500{
9501 struct strpush *sp;
9502 int len;
9503
9504 len = strlen(s);
9505 INT_OFF;
9506 if (g_parsefile->strpush) {
9507 sp = ckzalloc(sizeof(*sp));
9508 sp->prev = g_parsefile->strpush;
9509 } else {
9510 sp = &(g_parsefile->basestrpush);
9511 }
9512 g_parsefile->strpush = sp;
9513 sp->prev_string = g_parsefile->next_to_pgetc;
9514 sp->prev_left_in_line = g_parsefile->left_in_line;
9515#if ENABLE_ASH_ALIAS
9516 sp->ap = ap;
9517 if (ap) {
9518 ap->flag |= ALIASINUSE;
9519 sp->string = s;
9520 }
9521#endif
9522 g_parsefile->next_to_pgetc = s;
9523 g_parsefile->left_in_line = len;
9524 INT_ON;
9525}
9526
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009527static void
9528popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009529{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009530 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009531
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009532 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009533#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009534 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009535 if (g_parsefile->next_to_pgetc[-1] == ' '
9536 || g_parsefile->next_to_pgetc[-1] == '\t'
9537 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009538 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009539 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009540 if (sp->string != sp->ap->val) {
9541 free(sp->string);
9542 }
9543 sp->ap->flag &= ~ALIASINUSE;
9544 if (sp->ap->flag & ALIASDEAD) {
9545 unalias(sp->ap->name);
9546 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009547 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009548#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009549 g_parsefile->next_to_pgetc = sp->prev_string;
9550 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009551 g_parsefile->strpush = sp->prev;
9552 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009553 free(sp);
9554 INT_ON;
9555}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009556
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009557//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9558//it peeks whether it is &>, and then pushes back both chars.
9559//This function needs to save last *next_to_pgetc to buf[0]
9560//to make two pungetc() reliable. Currently,
9561// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009562static int
9563preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009564{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009565 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009566 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009567
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009568 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009569#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009570 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009571 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9572 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009573 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009574#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009575 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009576#endif
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02009577 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009578 if (nr == 0) {
9579 /* Ctrl+C pressed */
9580 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009581 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009582 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009583 raise(SIGINT);
9584 return 1;
9585 }
Eric Andersenc470f442003-07-28 09:56:35 +00009586 goto retry;
9587 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009588 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009589 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009590 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009591 }
Eric Andersencb57d552001-06-28 07:25:16 +00009592 }
9593#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009594 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009595#endif
9596
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009597#if 0
9598/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009599 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009600 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009601 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009602 if (flags >= 0 && (flags & O_NONBLOCK)) {
9603 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009604 if (fcntl(0, F_SETFL, flags) >= 0) {
9605 out2str("sh: turning off NDELAY mode\n");
9606 goto retry;
9607 }
9608 }
9609 }
9610 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009611#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009612 return nr;
9613}
9614
9615/*
9616 * Refill the input buffer and return the next input character:
9617 *
9618 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009619 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9620 * or we are reading from a string so we can't refill the buffer,
9621 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009622 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009623 * 4) Process input up to the next newline, deleting nul characters.
9624 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009625//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9626#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009627static int
Eric Andersenc470f442003-07-28 09:56:35 +00009628preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009629{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009630 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009631 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009632
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009633 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009634#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009635 if (g_parsefile->left_in_line == -1
9636 && g_parsefile->strpush->ap
9637 && g_parsefile->next_to_pgetc[-1] != ' '
9638 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009639 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009640 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009641 return PEOA;
9642 }
Eric Andersen2870d962001-07-02 17:27:21 +00009643#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009644 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009645 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009646 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9647 g_parsefile->left_in_line,
9648 g_parsefile->next_to_pgetc,
9649 g_parsefile->next_to_pgetc);
9650 if (--g_parsefile->left_in_line >= 0)
9651 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009652 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009653 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009654 * "pgetc" needs refilling.
9655 */
9656
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009657 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009658 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009659 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009660 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009661 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009662 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009663 /* even in failure keep left_in_line and next_to_pgetc
9664 * in lock step, for correct multi-layer pungetc.
9665 * left_in_line was decremented before preadbuffer(),
9666 * must inc next_to_pgetc: */
9667 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009668 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009669 }
Eric Andersencb57d552001-06-28 07:25:16 +00009670
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009671 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009672 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009673 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009674 again:
9675 more = preadfd();
9676 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009677 /* don't try reading again */
9678 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009679 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009680 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009681 return PEOF;
9682 }
9683 }
9684
Denis Vlasenko727752d2008-11-28 03:41:47 +00009685 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009686 * Set g_parsefile->left_in_line
9687 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009688 * NUL chars are deleted.
9689 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009690 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009691 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009692 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009693
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009694 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009695
Denis Vlasenko727752d2008-11-28 03:41:47 +00009696 c = *q;
9697 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009698 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009699 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009700 q++;
9701 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009702 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009703 break;
9704 }
Eric Andersencb57d552001-06-28 07:25:16 +00009705 }
9706
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009707 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009708 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9709 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009710 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009711 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009712 }
9713 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009714 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009715
Eric Andersencb57d552001-06-28 07:25:16 +00009716 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009717 char save = *q;
9718 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009719 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009720 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009721 }
9722
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009723 pgetc_debug("preadbuffer at %d:%p'%s'",
9724 g_parsefile->left_in_line,
9725 g_parsefile->next_to_pgetc,
9726 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009727 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009728}
9729
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009730#define pgetc_as_macro() \
9731 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009732 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009733 : preadbuffer() \
9734 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009735
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009736static int
9737pgetc(void)
9738{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009739 pgetc_debug("pgetc_fast at %d:%p'%s'",
9740 g_parsefile->left_in_line,
9741 g_parsefile->next_to_pgetc,
9742 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009743 return pgetc_as_macro();
9744}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009745
9746#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009747# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009748#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009749# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009750#endif
9751
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009752#if ENABLE_ASH_ALIAS
9753static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009754pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009755{
9756 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009757 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009758 pgetc_debug("pgetc_fast at %d:%p'%s'",
9759 g_parsefile->left_in_line,
9760 g_parsefile->next_to_pgetc,
9761 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009762 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009763 } while (c == PEOA);
9764 return c;
9765}
9766#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009767# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009768#endif
9769
9770/*
9771 * Read a line from the script.
9772 */
9773static char *
9774pfgets(char *line, int len)
9775{
9776 char *p = line;
9777 int nleft = len;
9778 int c;
9779
9780 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009781 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009782 if (c == PEOF) {
9783 if (p == line)
9784 return NULL;
9785 break;
9786 }
9787 *p++ = c;
9788 if (c == '\n')
9789 break;
9790 }
9791 *p = '\0';
9792 return line;
9793}
9794
Eric Andersenc470f442003-07-28 09:56:35 +00009795/*
9796 * Undo the last call to pgetc. Only one character may be pushed back.
9797 * PEOF may be pushed back.
9798 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009799static void
Eric Andersenc470f442003-07-28 09:56:35 +00009800pungetc(void)
9801{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009802 g_parsefile->left_in_line++;
9803 g_parsefile->next_to_pgetc--;
9804 pgetc_debug("pushed back to %d:%p'%s'",
9805 g_parsefile->left_in_line,
9806 g_parsefile->next_to_pgetc,
9807 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009808}
9809
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009810/*
9811 * To handle the "." command, a stack of input files is used. Pushfile
9812 * adds a new entry to the stack and popfile restores the previous level.
9813 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009814static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009815pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009816{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009817 struct parsefile *pf;
9818
Denis Vlasenko597906c2008-02-20 16:38:54 +00009819 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009820 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009821 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009822 /*pf->strpush = NULL; - ckzalloc did it */
9823 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009824 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009825}
9826
9827static void
9828popfile(void)
9829{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009830 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009831
Denis Vlasenkob012b102007-02-19 22:43:01 +00009832 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009833 if (pf->pf_fd >= 0)
9834 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009835 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009836 while (pf->strpush)
9837 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009838 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009839 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009840 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009841}
9842
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009843/*
9844 * Return to top level.
9845 */
9846static void
9847popallfiles(void)
9848{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009849 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009850 popfile();
9851}
9852
9853/*
9854 * Close the file(s) that the shell is reading commands from. Called
9855 * after a fork is done.
9856 */
9857static void
9858closescript(void)
9859{
9860 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009861 if (g_parsefile->pf_fd > 0) {
9862 close(g_parsefile->pf_fd);
9863 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009864 }
9865}
9866
9867/*
9868 * Like setinputfile, but takes an open file descriptor. Call this with
9869 * interrupts off.
9870 */
9871static void
9872setinputfd(int fd, int push)
9873{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009874 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009875 if (push) {
9876 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009877 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009878 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009879 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009880 if (g_parsefile->buf == NULL)
9881 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009882 g_parsefile->left_in_buffer = 0;
9883 g_parsefile->left_in_line = 0;
9884 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009885}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009886
Eric Andersenc470f442003-07-28 09:56:35 +00009887/*
9888 * Set the input to take input from a file. If push is set, push the
9889 * old input onto the stack first.
9890 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009891static int
9892setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009893{
9894 int fd;
9895 int fd2;
9896
Denis Vlasenkob012b102007-02-19 22:43:01 +00009897 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009898 fd = open(fname, O_RDONLY);
9899 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009900 if (flags & INPUT_NOFILE_OK)
9901 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009902 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009903 }
Eric Andersenc470f442003-07-28 09:56:35 +00009904 if (fd < 10) {
9905 fd2 = copyfd(fd, 10);
9906 close(fd);
9907 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009908 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009909 fd = fd2;
9910 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009911 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009912 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009913 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009914 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009915}
9916
Eric Andersencb57d552001-06-28 07:25:16 +00009917/*
9918 * Like setinputfile, but takes input from a string.
9919 */
Eric Andersenc470f442003-07-28 09:56:35 +00009920static void
9921setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009922{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009923 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009924 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009925 g_parsefile->next_to_pgetc = string;
9926 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009927 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009928 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009929 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009930}
9931
9932
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009933/* ============ mail.c
9934 *
9935 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009936 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009937
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009938#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009939
Eric Andersencb57d552001-06-28 07:25:16 +00009940#define MAXMBOXES 10
9941
Eric Andersenc470f442003-07-28 09:56:35 +00009942/* times of mailboxes */
9943static time_t mailtime[MAXMBOXES];
9944/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009945static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009946
Eric Andersencb57d552001-06-28 07:25:16 +00009947/*
Eric Andersenc470f442003-07-28 09:56:35 +00009948 * Print appropriate message(s) if mail has arrived.
9949 * If mail_var_path_changed is set,
9950 * then the value of MAIL has mail_var_path_changed,
9951 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009952 */
Eric Andersenc470f442003-07-28 09:56:35 +00009953static void
9954chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009955{
Eric Andersencb57d552001-06-28 07:25:16 +00009956 const char *mpath;
9957 char *p;
9958 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009959 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009960 struct stackmark smark;
9961 struct stat statb;
9962
Eric Andersencb57d552001-06-28 07:25:16 +00009963 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009964 mpath = mpathset() ? mpathval() : mailval();
9965 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02009966 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00009967 if (p == NULL)
9968 break;
9969 if (*p == '\0')
9970 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009971 for (q = p; *q; q++)
9972 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009973#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009974 if (q[-1] != '/')
9975 abort();
9976#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009977 q[-1] = '\0'; /* delete trailing '/' */
9978 if (stat(p, &statb) < 0) {
9979 *mtp = 0;
9980 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009981 }
Eric Andersenc470f442003-07-28 09:56:35 +00009982 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9983 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02009984 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +00009985 pathopt ? pathopt : "you have mail"
9986 );
9987 }
9988 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009989 }
Eric Andersenc470f442003-07-28 09:56:35 +00009990 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009991 popstackmark(&smark);
9992}
Eric Andersencb57d552001-06-28 07:25:16 +00009993
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009994static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009995changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009996{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009997 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009998}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009999
Denis Vlasenko131ae172007-02-18 13:00:19 +000010000#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010001
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010002
10003/* ============ ??? */
10004
Eric Andersencb57d552001-06-28 07:25:16 +000010005/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010006 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010007 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010008static void
10009setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010010{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010011 char **newparam;
10012 char **ap;
10013 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010014
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010015 for (nparam = 0; argv[nparam]; nparam++)
10016 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010017 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10018 while (*argv) {
10019 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010020 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010021 *ap = NULL;
10022 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010023 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010024 shellparam.nparam = nparam;
10025 shellparam.p = newparam;
10026#if ENABLE_ASH_GETOPTS
10027 shellparam.optind = 1;
10028 shellparam.optoff = -1;
10029#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010030}
10031
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010032/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010033 * Process shell options. The global variable argptr contains a pointer
10034 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010035 *
10036 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10037 * For a non-interactive shell, an error condition encountered
10038 * by a special built-in ... shall cause the shell to write a diagnostic message
10039 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010040 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010041 * ...
10042 * Utility syntax error (option or operand error) Shall exit
10043 * ...
10044 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10045 * we see that bash does not do that (set "finishes" with error code 1 instead,
10046 * and shell continues), and people rely on this behavior!
10047 * Testcase:
10048 * set -o barfoo 2>/dev/null
10049 * echo $?
10050 *
10051 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010052 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010053static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010054plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010055{
10056 int i;
10057
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010058 if (name) {
10059 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010060 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010061 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010062 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010063 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010064 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010065 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010066 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010067 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010068 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010069 if (val) {
10070 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10071 } else {
10072 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10073 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010074 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010075 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010076}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010077static void
10078setoption(int flag, int val)
10079{
10080 int i;
10081
10082 for (i = 0; i < NOPTS; i++) {
10083 if (optletters(i) == flag) {
10084 optlist[i] = val;
10085 return;
10086 }
10087 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010088 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010089 /* NOTREACHED */
10090}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010091static int
Eric Andersenc470f442003-07-28 09:56:35 +000010092options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010093{
10094 char *p;
10095 int val;
10096 int c;
10097
10098 if (cmdline)
10099 minusc = NULL;
10100 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010101 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010102 if (c != '-' && c != '+')
10103 break;
10104 argptr++;
10105 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010106 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010107 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010108 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010109 if (!cmdline) {
10110 /* "-" means turn off -x and -v */
10111 if (p[0] == '\0')
10112 xflag = vflag = 0;
10113 /* "--" means reset params */
10114 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010115 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010116 }
Eric Andersenc470f442003-07-28 09:56:35 +000010117 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010118 }
Eric Andersencb57d552001-06-28 07:25:16 +000010119 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010120 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010121 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010122 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010123 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010124 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010125 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010126 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010127 /* it already printed err message */
10128 return 1; /* error */
10129 }
Eric Andersencb57d552001-06-28 07:25:16 +000010130 if (*argptr)
10131 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010132 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10133 isloginsh = 1;
10134 /* bash does not accept +-login, we also won't */
10135 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010136 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010137 isloginsh = 1;
10138 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010139 } else {
10140 setoption(c, val);
10141 }
10142 }
10143 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010144 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010145}
10146
Eric Andersencb57d552001-06-28 07:25:16 +000010147/*
Eric Andersencb57d552001-06-28 07:25:16 +000010148 * The shift builtin command.
10149 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010150static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010151shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010152{
10153 int n;
10154 char **ap1, **ap2;
10155
10156 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010157 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010158 n = number(argv[1]);
10159 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010160 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010161 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010162 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010163 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010164 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010165 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010166 }
10167 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010168 while ((*ap2++ = *ap1++) != NULL)
10169 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010170#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010171 shellparam.optind = 1;
10172 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010173#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010174 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010175 return 0;
10176}
10177
Eric Andersencb57d552001-06-28 07:25:16 +000010178/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010179 * POSIX requires that 'set' (but not export or readonly) output the
10180 * variables in lexicographic order - by the locale's collating order (sigh).
10181 * Maybe we could keep them in an ordered balanced binary tree
10182 * instead of hashed lists.
10183 * For now just roll 'em through qsort for printing...
10184 */
10185static int
10186showvars(const char *sep_prefix, int on, int off)
10187{
10188 const char *sep;
10189 char **ep, **epend;
10190
10191 ep = listvars(on, off, &epend);
10192 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10193
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010194 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010195
10196 for (; ep < epend; ep++) {
10197 const char *p;
10198 const char *q;
10199
10200 p = strchrnul(*ep, '=');
10201 q = nullstr;
10202 if (*p)
10203 q = single_quote(++p);
10204 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10205 }
10206 return 0;
10207}
10208
10209/*
Eric Andersencb57d552001-06-28 07:25:16 +000010210 * The set command builtin.
10211 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010212static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010213setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010214{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010215 int retval;
10216
Denis Vlasenko68404f12008-03-17 09:00:54 +000010217 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010218 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010219 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010220 retval = 1;
10221 if (!options(0)) { /* if no parse error... */
10222 retval = 0;
10223 optschanged();
10224 if (*argptr != NULL) {
10225 setparam(argptr);
10226 }
Eric Andersencb57d552001-06-28 07:25:16 +000010227 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010228 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010229 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010230}
10231
Denis Vlasenko131ae172007-02-18 13:00:19 +000010232#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010233static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010234change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010235{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010236 uint32_t t;
10237
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010238 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010239 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010240 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010241 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010242 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010243 vrandom.flags &= ~VNOFUNC;
10244 } else {
10245 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010246 t = strtoul(value, NULL, 10);
10247 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010248 }
Eric Andersenef02f822004-03-11 13:34:24 +000010249}
Eric Andersen16767e22004-03-16 05:14:10 +000010250#endif
10251
Denis Vlasenko131ae172007-02-18 13:00:19 +000010252#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010253static int
Eric Andersenc470f442003-07-28 09:56:35 +000010254getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010255{
10256 char *p, *q;
10257 char c = '?';
10258 int done = 0;
10259 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010260 char s[12];
10261 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010262
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010263 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010264 return 1;
10265 optnext = optfirst + *param_optind - 1;
10266
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010267 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010268 p = NULL;
10269 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010270 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010271 if (p == NULL || *p == '\0') {
10272 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010273 p = *optnext;
10274 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010275 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010276 p = NULL;
10277 done = 1;
10278 goto out;
10279 }
10280 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010281 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010282 goto atend;
10283 }
10284
10285 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010286 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010287 if (*q == '\0') {
10288 if (optstr[0] == ':') {
10289 s[0] = c;
10290 s[1] = '\0';
10291 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010292 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010293 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010294 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010295 }
10296 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010297 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010298 }
10299 if (*++q == ':')
10300 q++;
10301 }
10302
10303 if (*++q == ':') {
10304 if (*p == '\0' && (p = *optnext) == NULL) {
10305 if (optstr[0] == ':') {
10306 s[0] = c;
10307 s[1] = '\0';
10308 err |= setvarsafe("OPTARG", s, 0);
10309 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010310 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010311 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010312 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010313 c = '?';
10314 }
Eric Andersenc470f442003-07-28 09:56:35 +000010315 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010316 }
10317
10318 if (p == *optnext)
10319 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010320 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010321 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010322 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010323 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010324 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010325 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010326 *param_optind = optnext - optfirst + 1;
10327 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010328 err |= setvarsafe("OPTIND", s, VNOFUNC);
10329 s[0] = c;
10330 s[1] = '\0';
10331 err |= setvarsafe(optvar, s, 0);
10332 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010333 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010334 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010335 flush_stdout_stderr();
10336 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010337 }
10338 return done;
10339}
Eric Andersenc470f442003-07-28 09:56:35 +000010340
10341/*
10342 * The getopts builtin. Shellparam.optnext points to the next argument
10343 * to be processed. Shellparam.optptr points to the next character to
10344 * be processed in the current argument. If shellparam.optnext is NULL,
10345 * then it's the first time getopts has been called.
10346 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010347static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010348getoptscmd(int argc, char **argv)
10349{
10350 char **optbase;
10351
10352 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010353 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010354 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010355 optbase = shellparam.p;
10356 if (shellparam.optind > shellparam.nparam + 1) {
10357 shellparam.optind = 1;
10358 shellparam.optoff = -1;
10359 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010360 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010361 optbase = &argv[3];
10362 if (shellparam.optind > argc - 2) {
10363 shellparam.optind = 1;
10364 shellparam.optoff = -1;
10365 }
10366 }
10367
10368 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010369 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010370}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010371#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010372
Eric Andersencb57d552001-06-28 07:25:16 +000010373
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010374/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010375
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010376struct heredoc {
10377 struct heredoc *next; /* next here document in list */
10378 union node *here; /* redirection node */
10379 char *eofmark; /* string indicating end of input */
10380 smallint striptabs; /* if set, strip leading tabs */
10381};
10382
10383static smallint tokpushback; /* last token pushed back */
10384static smallint parsebackquote; /* nonzero if we are inside backquotes */
10385static smallint quoteflag; /* set if (part of) last token was quoted */
10386static token_id_t lasttoken; /* last token read (integer id Txxx) */
10387static struct heredoc *heredoclist; /* list of here documents to read */
10388static char *wordtext; /* text of last word returned by readtoken */
10389static struct nodelist *backquotelist;
10390static union node *redirnode;
10391static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010392
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010393static const char *
10394tokname(char *buf, int tok)
10395{
10396 if (tok < TSEMI)
10397 return tokname_array[tok] + 1;
10398 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10399 return buf;
10400}
10401
10402/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010403 * Called when an unexpected token is read during the parse. The argument
10404 * is the token that is expected, or -1 if more than one type of token can
10405 * occur at this point.
10406 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010407static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010408static void
10409raise_error_unexpected_syntax(int token)
10410{
10411 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010412 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010413 int l;
10414
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010415 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010416 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010417 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010418 raise_error_syntax(msg);
10419 /* NOTREACHED */
10420}
Eric Andersencb57d552001-06-28 07:25:16 +000010421
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010422#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010423
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010424/* parsing is heavily cross-recursive, need these forward decls */
10425static union node *andor(void);
10426static union node *pipeline(void);
10427static union node *parse_command(void);
10428static void parseheredoc(void);
10429static char peektoken(void);
10430static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010431
Eric Andersenc470f442003-07-28 09:56:35 +000010432static union node *
10433list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010434{
10435 union node *n1, *n2, *n3;
10436 int tok;
10437
Eric Andersenc470f442003-07-28 09:56:35 +000010438 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10439 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010440 return NULL;
10441 n1 = NULL;
10442 for (;;) {
10443 n2 = andor();
10444 tok = readtoken();
10445 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010446 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010447 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010448 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010449 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010450 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010451 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010452 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010453 n2 = n3;
10454 }
10455 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010456 }
10457 }
10458 if (n1 == NULL) {
10459 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010460 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010461 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010462 n3->type = NSEMI;
10463 n3->nbinary.ch1 = n1;
10464 n3->nbinary.ch2 = n2;
10465 n1 = n3;
10466 }
10467 switch (tok) {
10468 case TBACKGND:
10469 case TSEMI:
10470 tok = readtoken();
10471 /* fall through */
10472 case TNL:
10473 if (tok == TNL) {
10474 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010475 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010476 return n1;
10477 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010478 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010479 }
Eric Andersenc470f442003-07-28 09:56:35 +000010480 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010481 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010482 return n1;
10483 break;
10484 case TEOF:
10485 if (heredoclist)
10486 parseheredoc();
10487 else
Eric Andersenc470f442003-07-28 09:56:35 +000010488 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010489 return n1;
10490 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010491 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010492 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010493 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010494 return n1;
10495 }
10496 }
10497}
10498
Eric Andersenc470f442003-07-28 09:56:35 +000010499static union node *
10500andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010501{
Eric Andersencb57d552001-06-28 07:25:16 +000010502 union node *n1, *n2, *n3;
10503 int t;
10504
Eric Andersencb57d552001-06-28 07:25:16 +000010505 n1 = pipeline();
10506 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010507 t = readtoken();
10508 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010509 t = NAND;
10510 } else if (t == TOR) {
10511 t = NOR;
10512 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010513 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010514 return n1;
10515 }
Eric Andersenc470f442003-07-28 09:56:35 +000010516 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010517 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010518 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010519 n3->type = t;
10520 n3->nbinary.ch1 = n1;
10521 n3->nbinary.ch2 = n2;
10522 n1 = n3;
10523 }
10524}
10525
Eric Andersenc470f442003-07-28 09:56:35 +000010526static union node *
10527pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010528{
Eric Andersencb57d552001-06-28 07:25:16 +000010529 union node *n1, *n2, *pipenode;
10530 struct nodelist *lp, *prev;
10531 int negate;
10532
10533 negate = 0;
10534 TRACE(("pipeline: entered\n"));
10535 if (readtoken() == TNOT) {
10536 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010537 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010538 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010539 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010540 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010541 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010542 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010543 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010544 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010545 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010546 pipenode->npipe.cmdlist = lp;
10547 lp->n = n1;
10548 do {
10549 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010550 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010551 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010552 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010553 prev->next = lp;
10554 } while (readtoken() == TPIPE);
10555 lp->next = NULL;
10556 n1 = pipenode;
10557 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010558 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010559 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010560 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010561 n2->type = NNOT;
10562 n2->nnot.com = n1;
10563 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010564 }
10565 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010566}
10567
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010568static union node *
10569makename(void)
10570{
10571 union node *n;
10572
Denis Vlasenko597906c2008-02-20 16:38:54 +000010573 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010574 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010575 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010576 n->narg.text = wordtext;
10577 n->narg.backquote = backquotelist;
10578 return n;
10579}
10580
10581static void
10582fixredir(union node *n, const char *text, int err)
10583{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010584 int fd;
10585
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010586 TRACE(("Fix redir %s %d\n", text, err));
10587 if (!err)
10588 n->ndup.vname = NULL;
10589
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010590 fd = bb_strtou(text, NULL, 10);
10591 if (!errno && fd >= 0)
10592 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010593 else if (LONE_DASH(text))
10594 n->ndup.dupfd = -1;
10595 else {
10596 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010597 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010598 n->ndup.vname = makename();
10599 }
10600}
10601
10602/*
10603 * Returns true if the text contains nothing to expand (no dollar signs
10604 * or backquotes).
10605 */
10606static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010607noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010608{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010609 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010610
Denys Vlasenkocd716832009-11-28 22:14:02 +010010611 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010612 if (c == CTLQUOTEMARK)
10613 continue;
10614 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010615 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010616 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010617 return 0;
10618 }
10619 return 1;
10620}
10621
10622static void
10623parsefname(void)
10624{
10625 union node *n = redirnode;
10626
10627 if (readtoken() != TWORD)
10628 raise_error_unexpected_syntax(-1);
10629 if (n->type == NHERE) {
10630 struct heredoc *here = heredoc;
10631 struct heredoc *p;
10632 int i;
10633
10634 if (quoteflag == 0)
10635 n->type = NXHERE;
10636 TRACE(("Here document %d\n", n->type));
10637 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010638 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010639 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010640 here->eofmark = wordtext;
10641 here->next = NULL;
10642 if (heredoclist == NULL)
10643 heredoclist = here;
10644 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010645 for (p = heredoclist; p->next; p = p->next)
10646 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010647 p->next = here;
10648 }
10649 } else if (n->type == NTOFD || n->type == NFROMFD) {
10650 fixredir(n, wordtext, 0);
10651 } else {
10652 n->nfile.fname = makename();
10653 }
10654}
Eric Andersencb57d552001-06-28 07:25:16 +000010655
Eric Andersenc470f442003-07-28 09:56:35 +000010656static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010657simplecmd(void)
10658{
10659 union node *args, **app;
10660 union node *n = NULL;
10661 union node *vars, **vpp;
10662 union node **rpp, *redir;
10663 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010664#if ENABLE_ASH_BASH_COMPAT
10665 smallint double_brackets_flag = 0;
10666#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010667
10668 args = NULL;
10669 app = &args;
10670 vars = NULL;
10671 vpp = &vars;
10672 redir = NULL;
10673 rpp = &redir;
10674
10675 savecheckkwd = CHKALIAS;
10676 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010677 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010678 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010679 t = readtoken();
10680 switch (t) {
10681#if ENABLE_ASH_BASH_COMPAT
10682 case TAND: /* "&&" */
10683 case TOR: /* "||" */
10684 if (!double_brackets_flag) {
10685 tokpushback = 1;
10686 goto out;
10687 }
10688 wordtext = (char *) (t == TAND ? "-a" : "-o");
10689#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010690 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010691 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010692 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010693 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010694 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010695#if ENABLE_ASH_BASH_COMPAT
10696 if (strcmp("[[", wordtext) == 0)
10697 double_brackets_flag = 1;
10698 else if (strcmp("]]", wordtext) == 0)
10699 double_brackets_flag = 0;
10700#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010701 n->narg.backquote = backquotelist;
10702 if (savecheckkwd && isassignment(wordtext)) {
10703 *vpp = n;
10704 vpp = &n->narg.next;
10705 } else {
10706 *app = n;
10707 app = &n->narg.next;
10708 savecheckkwd = 0;
10709 }
10710 break;
10711 case TREDIR:
10712 *rpp = n = redirnode;
10713 rpp = &n->nfile.next;
10714 parsefname(); /* read name of redirection file */
10715 break;
10716 case TLP:
10717 if (args && app == &args->narg.next
10718 && !vars && !redir
10719 ) {
10720 struct builtincmd *bcmd;
10721 const char *name;
10722
10723 /* We have a function */
10724 if (readtoken() != TRP)
10725 raise_error_unexpected_syntax(TRP);
10726 name = n->narg.text;
10727 if (!goodname(name)
10728 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10729 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010730 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010731 }
10732 n->type = NDEFUN;
10733 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10734 n->narg.next = parse_command();
10735 return n;
10736 }
10737 /* fall through */
10738 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010739 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010740 goto out;
10741 }
10742 }
10743 out:
10744 *app = NULL;
10745 *vpp = NULL;
10746 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010747 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010748 n->type = NCMD;
10749 n->ncmd.args = args;
10750 n->ncmd.assign = vars;
10751 n->ncmd.redirect = redir;
10752 return n;
10753}
10754
10755static union node *
10756parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010757{
Eric Andersencb57d552001-06-28 07:25:16 +000010758 union node *n1, *n2;
10759 union node *ap, **app;
10760 union node *cp, **cpp;
10761 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010762 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010763 int t;
10764
10765 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010766 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010767
Eric Andersencb57d552001-06-28 07:25:16 +000010768 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010769 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010770 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010771 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010772 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010773 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010774 n1->type = NIF;
10775 n1->nif.test = list(0);
10776 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010777 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010778 n1->nif.ifpart = list(0);
10779 n2 = n1;
10780 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010781 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010782 n2 = n2->nif.elsepart;
10783 n2->type = NIF;
10784 n2->nif.test = list(0);
10785 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010786 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010787 n2->nif.ifpart = list(0);
10788 }
10789 if (lasttoken == TELSE)
10790 n2->nif.elsepart = list(0);
10791 else {
10792 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010793 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010794 }
Eric Andersenc470f442003-07-28 09:56:35 +000010795 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010796 break;
10797 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010798 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010799 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010800 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010801 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010802 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010803 got = readtoken();
10804 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010805 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010806 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010807 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010808 }
10809 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010810 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010811 break;
10812 }
10813 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010814 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010815 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010816 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010817 n1->type = NFOR;
10818 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010819 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010820 if (readtoken() == TIN) {
10821 app = &ap;
10822 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010823 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010824 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010825 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010826 n2->narg.text = wordtext;
10827 n2->narg.backquote = backquotelist;
10828 *app = n2;
10829 app = &n2->narg.next;
10830 }
10831 *app = NULL;
10832 n1->nfor.args = ap;
10833 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010834 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010835 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010836 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010837 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010838 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010839 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010840 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010841 n1->nfor.args = n2;
10842 /*
10843 * Newline or semicolon here is optional (but note
10844 * that the original Bourne shell only allowed NL).
10845 */
10846 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010847 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010848 }
Eric Andersenc470f442003-07-28 09:56:35 +000010849 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010850 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010851 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010852 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010853 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010854 break;
10855 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010856 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010857 n1->type = NCASE;
10858 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010859 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010860 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010861 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010862 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010863 n2->narg.text = wordtext;
10864 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010865 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010866 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010867 } while (readtoken() == TNL);
10868 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010869 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010870 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010871 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010872 checkkwd = CHKNL | CHKKWD;
10873 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010874 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010875 if (lasttoken == TLP)
10876 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010877 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010878 cp->type = NCLIST;
10879 app = &cp->nclist.pattern;
10880 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010881 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010882 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010883 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010884 ap->narg.text = wordtext;
10885 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010886 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010887 break;
10888 app = &ap->narg.next;
10889 readtoken();
10890 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010891 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010892 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010893 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010894 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010895
Eric Andersenc470f442003-07-28 09:56:35 +000010896 cpp = &cp->nclist.next;
10897
10898 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010899 t = readtoken();
10900 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010901 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010902 raise_error_unexpected_syntax(TENDCASE);
10903 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010904 }
Eric Andersenc470f442003-07-28 09:56:35 +000010905 }
Eric Andersencb57d552001-06-28 07:25:16 +000010906 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010907 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010908 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010909 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010910 n1->type = NSUBSHELL;
10911 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010912 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010913 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010914 break;
10915 case TBEGIN:
10916 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010917 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010918 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010919 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010920 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010921 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010922 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010923 }
10924
Eric Andersenc470f442003-07-28 09:56:35 +000010925 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010926 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010927
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010928 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010929 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010930 checkkwd = CHKKWD | CHKALIAS;
10931 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010932 while (readtoken() == TREDIR) {
10933 *rpp = n2 = redirnode;
10934 rpp = &n2->nfile.next;
10935 parsefname();
10936 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010937 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010938 *rpp = NULL;
10939 if (redir) {
10940 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010941 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010942 n2->type = NREDIR;
10943 n2->nredir.n = n1;
10944 n1 = n2;
10945 }
10946 n1->nredir.redirect = redir;
10947 }
Eric Andersencb57d552001-06-28 07:25:16 +000010948 return n1;
10949}
10950
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010951#if ENABLE_ASH_BASH_COMPAT
10952static int decode_dollar_squote(void)
10953{
10954 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10955 int c, cnt;
10956 char *p;
10957 char buf[4];
10958
10959 c = pgetc();
10960 p = strchr(C_escapes, c);
10961 if (p) {
10962 buf[0] = c;
10963 p = buf;
10964 cnt = 3;
10965 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10966 do {
10967 c = pgetc();
10968 *++p = c;
10969 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10970 pungetc();
10971 } else if (c == 'x') { /* \xHH */
10972 do {
10973 c = pgetc();
10974 *++p = c;
10975 } while (isxdigit(c) && --cnt);
10976 pungetc();
10977 if (cnt == 3) { /* \x but next char is "bad" */
10978 c = 'x';
10979 goto unrecognized;
10980 }
10981 } else { /* simple seq like \\ or \t */
10982 p++;
10983 }
10984 *p = '\0';
10985 p = buf;
10986 c = bb_process_escape_sequence((void*)&p);
10987 } else { /* unrecognized "\z": print both chars unless ' or " */
10988 if (c != '\'' && c != '"') {
10989 unrecognized:
10990 c |= 0x100; /* "please encode \, then me" */
10991 }
10992 }
10993 return c;
10994}
10995#endif
10996
Eric Andersencb57d552001-06-28 07:25:16 +000010997/*
10998 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10999 * is not NULL, read a here document. In the latter case, eofmark is the
11000 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011001 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011002 * is the first character of the input token or document.
11003 *
11004 * Because C does not have internal subroutines, I have simulated them
11005 * using goto's to implement the subroutine linkage. The following macros
11006 * will run code that appears at the end of readtoken1.
11007 */
Eric Andersen2870d962001-07-02 17:27:21 +000011008#define CHECKEND() {goto checkend; checkend_return:;}
11009#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11010#define PARSESUB() {goto parsesub; parsesub_return:;}
11011#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11012#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11013#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011014static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011015readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011016{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011017 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011018 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011019 char *out;
11020 int len;
11021 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011022 struct nodelist *bqlist;
11023 smallint quotef;
11024 smallint dblquote;
11025 smallint oldstyle;
11026 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011027#if ENABLE_ASH_EXPAND_PRMT
11028 smallint pssyntax; /* we are expanding a prompt string */
11029#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011030 int varnest; /* levels of variables expansion */
11031 int arinest; /* levels of arithmetic expansion */
11032 int parenlevel; /* levels of parens in arithmetic */
11033 int dqvarnest; /* levels of variables expansion within double quotes */
11034
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011035 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011036
Eric Andersencb57d552001-06-28 07:25:16 +000011037#if __GNUC__
11038 /* Avoid longjmp clobbering */
11039 (void) &out;
11040 (void) &quotef;
11041 (void) &dblquote;
11042 (void) &varnest;
11043 (void) &arinest;
11044 (void) &parenlevel;
11045 (void) &dqvarnest;
11046 (void) &oldstyle;
11047 (void) &prevsyntax;
11048 (void) &syntax;
11049#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011050 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011051 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011052 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011053 oldstyle = 0;
11054 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011055#if ENABLE_ASH_EXPAND_PRMT
11056 pssyntax = (syntax == PSSYNTAX);
11057 if (pssyntax)
11058 syntax = DQSYNTAX;
11059#endif
11060 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011061 varnest = 0;
11062 arinest = 0;
11063 parenlevel = 0;
11064 dqvarnest = 0;
11065
11066 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011067 loop:
11068 /* For each line, until end of word */
11069 {
Eric Andersenc470f442003-07-28 09:56:35 +000011070 CHECKEND(); /* set c to PEOF if at end of here document */
11071 for (;;) { /* until end of line or end of word */
11072 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011073 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011074 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000011075 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000011076 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011077 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011078 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000011079 if (doprompt)
11080 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000011081 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011082 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011083 case CWORD:
11084 USTPUTC(c, out);
11085 break;
11086 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000011087 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000011088 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011089#if ENABLE_ASH_BASH_COMPAT
11090 if (c == '\\' && bash_dollar_squote) {
11091 c = decode_dollar_squote();
11092 if (c & 0x100) {
11093 USTPUTC('\\', out);
11094 c = (unsigned char)c;
11095 }
11096 }
11097#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011098 USTPUTC(c, out);
11099 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011100 case CBACK: /* backslash */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011101 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011102 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000011103 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011104 USTPUTC('\\', out);
11105 pungetc();
11106 } else if (c == '\n') {
11107 if (doprompt)
11108 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000011109 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000011110#if ENABLE_ASH_EXPAND_PRMT
11111 if (c == '$' && pssyntax) {
11112 USTPUTC(CTLESC, out);
11113 USTPUTC('\\', out);
11114 }
11115#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011116 if (dblquote && c != '\\'
11117 && c != '`' && c != '$'
11118 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011119 ) {
11120 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011121 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011122 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011123 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000011124 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011125 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011126 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011127 }
11128 break;
11129 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011130 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011131 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000011132 if (eofmark == NULL) {
11133 USTPUTC(CTLQUOTEMARK, out);
11134 }
Eric Andersencb57d552001-06-28 07:25:16 +000011135 break;
11136 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011137 syntax = DQSYNTAX;
11138 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011139 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000011140 case CENDQUOTE:
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011141 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011142 if (eofmark != NULL && arinest == 0
11143 && varnest == 0
11144 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000011145 USTPUTC(c, out);
11146 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011147 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000011148 syntax = BASESYNTAX;
11149 dblquote = 0;
11150 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011151 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011152 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000011153 }
11154 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011155 case CVAR: /* '$' */
11156 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000011157 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011158 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000011159 if (varnest > 0) {
11160 varnest--;
11161 if (dqvarnest > 0) {
11162 dqvarnest--;
11163 }
11164 USTPUTC(CTLENDVAR, out);
11165 } else {
11166 USTPUTC(c, out);
11167 }
11168 break;
Mike Frysinger98c52642009-04-02 10:02:37 +000011169#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011170 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000011171 parenlevel++;
11172 USTPUTC(c, out);
11173 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011174 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000011175 if (parenlevel > 0) {
11176 USTPUTC(c, out);
11177 --parenlevel;
11178 } else {
11179 if (pgetc() == ')') {
11180 if (--arinest == 0) {
11181 USTPUTC(CTLENDARI, out);
11182 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011183 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011184 } else
11185 USTPUTC(')', out);
11186 } else {
11187 /*
11188 * unbalanced parens
11189 * (don't 2nd guess - no error)
11190 */
11191 pungetc();
11192 USTPUTC(')', out);
11193 }
11194 }
11195 break;
11196#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011197 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000011198 PARSEBACKQOLD();
11199 break;
Eric Andersen2870d962001-07-02 17:27:21 +000011200 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000011201 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000011202 case CIGN:
11203 break;
11204 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000011205 if (varnest == 0) {
11206#if ENABLE_ASH_BASH_COMPAT
11207 if (c == '&') {
11208 if (pgetc() == '>')
11209 c = 0x100 + '>'; /* flag &> */
11210 pungetc();
11211 }
11212#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011213 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011214 }
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011215 IF_ASH_ALIAS(if (c != PEOA))
Eric Andersencb57d552001-06-28 07:25:16 +000011216 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000011217
Eric Andersencb57d552001-06-28 07:25:16 +000011218 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011219 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011220 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000011221 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011222 endword:
Mike Frysinger98c52642009-04-02 10:02:37 +000011223#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011224 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011225 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011226#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011227 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011228 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011229 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011230 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011231 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011232 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011233 }
11234 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011235 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011236 out = stackblock();
11237 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011238 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011239 && quotef == 0
11240 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011241 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011242 PARSEREDIR(); /* passed as params: out, c */
11243 lasttoken = TREDIR;
11244 return lasttoken;
11245 }
11246 /* else: non-number X seen, interpret it
11247 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011248 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011249 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011250 }
11251 quoteflag = quotef;
11252 backquotelist = bqlist;
11253 grabstackblock(len);
11254 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011255 lasttoken = TWORD;
11256 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011257/* end of readtoken routine */
11258
Eric Andersencb57d552001-06-28 07:25:16 +000011259/*
11260 * Check to see whether we are at the end of the here document. When this
11261 * is called, c is set to the first character of the next input line. If
11262 * we are at the end of the here document, this routine sets the c to PEOF.
11263 */
Eric Andersenc470f442003-07-28 09:56:35 +000011264checkend: {
11265 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011266#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011267 if (c == PEOA)
11268 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011269#endif
11270 if (striptabs) {
11271 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011272 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011273 }
Eric Andersenc470f442003-07-28 09:56:35 +000011274 }
11275 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011276 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011277 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011278
Eric Andersenc470f442003-07-28 09:56:35 +000011279 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011280 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11281 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011282 if (*p == '\n' && *q == '\0') {
11283 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011284 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011285 needprompt = doprompt;
11286 } else {
11287 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011288 }
11289 }
11290 }
11291 }
Eric Andersenc470f442003-07-28 09:56:35 +000011292 goto checkend_return;
11293}
Eric Andersencb57d552001-06-28 07:25:16 +000011294
Eric Andersencb57d552001-06-28 07:25:16 +000011295/*
11296 * Parse a redirection operator. The variable "out" points to a string
11297 * specifying the fd to be redirected. The variable "c" contains the
11298 * first character of the redirection operator.
11299 */
Eric Andersenc470f442003-07-28 09:56:35 +000011300parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011301 /* out is already checked to be a valid number or "" */
11302 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011303 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011304
Denis Vlasenko597906c2008-02-20 16:38:54 +000011305 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011306 if (c == '>') {
11307 np->nfile.fd = 1;
11308 c = pgetc();
11309 if (c == '>')
11310 np->type = NAPPEND;
11311 else if (c == '|')
11312 np->type = NCLOBBER;
11313 else if (c == '&')
11314 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011315 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011316 else {
11317 np->type = NTO;
11318 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011319 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011320 }
11321#if ENABLE_ASH_BASH_COMPAT
11322 else if (c == 0x100 + '>') { /* this flags &> redirection */
11323 np->nfile.fd = 1;
11324 pgetc(); /* this is '>', no need to check */
11325 np->type = NTO2;
11326 }
11327#endif
11328 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011329 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011330 c = pgetc();
11331 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011332 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011333 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011334 np = stzalloc(sizeof(struct nhere));
11335 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011336 }
11337 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011338 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011339 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011340 c = pgetc();
11341 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011342 heredoc->striptabs = 1;
11343 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011344 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011345 pungetc();
11346 }
11347 break;
11348
11349 case '&':
11350 np->type = NFROMFD;
11351 break;
11352
11353 case '>':
11354 np->type = NFROMTO;
11355 break;
11356
11357 default:
11358 np->type = NFROM;
11359 pungetc();
11360 break;
11361 }
Eric Andersencb57d552001-06-28 07:25:16 +000011362 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011363 if (fd >= 0)
11364 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011365 redirnode = np;
11366 goto parseredir_return;
11367}
Eric Andersencb57d552001-06-28 07:25:16 +000011368
Eric Andersencb57d552001-06-28 07:25:16 +000011369/*
11370 * Parse a substitution. At this point, we have read the dollar sign
11371 * and nothing else.
11372 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011373
11374/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11375 * (assuming ascii char codes, as the original implementation did) */
11376#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011377 (((unsigned)(c) - 33 < 32) \
11378 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011379parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011380 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011381 int typeloc;
11382 int flags;
11383 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011384 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011385
Eric Andersenc470f442003-07-28 09:56:35 +000011386 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011387 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011388 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011389 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011390#if ENABLE_ASH_BASH_COMPAT
11391 if (c == '\'')
11392 bash_dollar_squote = 1;
11393 else
11394#endif
11395 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011396 pungetc();
11397 } else if (c == '(') { /* $(command) or $((arith)) */
11398 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011399#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011400 PARSEARITH();
11401#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011402 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011403#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011404 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011405 pungetc();
11406 PARSEBACKQNEW();
11407 }
11408 } else {
11409 USTPUTC(CTLVAR, out);
11410 typeloc = out - (char *)stackblock();
11411 USTPUTC(VSNORMAL, out);
11412 subtype = VSNORMAL;
11413 if (c == '{') {
11414 c = pgetc();
11415 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011416 c = pgetc();
11417 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011418 c = '#';
11419 else
11420 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011421 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011422 subtype = 0;
11423 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011424 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011425 do {
11426 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011427 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011428 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011429 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011430 do {
11431 STPUTC(c, out);
11432 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011433 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011434 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011435 USTPUTC(c, out);
11436 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011437 } else {
11438 badsub:
11439 raise_error_syntax("bad substitution");
11440 }
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011441 if (c != '}' && subtype == VSLENGTH)
11442 goto badsub;
Eric Andersencb57d552001-06-28 07:25:16 +000011443
Eric Andersenc470f442003-07-28 09:56:35 +000011444 STPUTC('=', out);
11445 flags = 0;
11446 if (subtype == 0) {
11447 switch (c) {
11448 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011449 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011450#if ENABLE_ASH_BASH_COMPAT
11451 if (c == ':' || c == '$' || isdigit(c)) {
11452 pungetc();
11453 subtype = VSSUBSTR;
11454 break;
11455 }
11456#endif
11457 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011458 /*FALLTHROUGH*/
11459 default:
11460 p = strchr(types, c);
11461 if (p == NULL)
11462 goto badsub;
11463 subtype = p - types + VSNORMAL;
11464 break;
11465 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011466 case '#': {
11467 int cc = c;
11468 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11469 c = pgetc();
11470 if (c == cc)
11471 subtype++;
11472 else
11473 pungetc();
11474 break;
11475 }
11476#if ENABLE_ASH_BASH_COMPAT
11477 case '/':
11478 subtype = VSREPLACE;
11479 c = pgetc();
11480 if (c == '/')
11481 subtype++; /* VSREPLACEALL */
11482 else
11483 pungetc();
11484 break;
11485#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011486 }
Eric Andersenc470f442003-07-28 09:56:35 +000011487 } else {
11488 pungetc();
11489 }
11490 if (dblquote || arinest)
11491 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011492 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011493 if (subtype != VSNORMAL) {
11494 varnest++;
11495 if (dblquote || arinest) {
11496 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011497 }
11498 }
11499 }
Eric Andersenc470f442003-07-28 09:56:35 +000011500 goto parsesub_return;
11501}
Eric Andersencb57d552001-06-28 07:25:16 +000011502
Eric Andersencb57d552001-06-28 07:25:16 +000011503/*
11504 * Called to parse command substitutions. Newstyle is set if the command
11505 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11506 * list of commands (passed by reference), and savelen is the number of
11507 * characters on the top of the stack which must be preserved.
11508 */
Eric Andersenc470f442003-07-28 09:56:35 +000011509parsebackq: {
11510 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011511 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011512 union node *n;
11513 char *volatile str;
11514 struct jmploc jmploc;
11515 struct jmploc *volatile savehandler;
11516 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011517 smallint saveprompt = 0;
11518
Eric Andersencb57d552001-06-28 07:25:16 +000011519#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011520 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011521#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011522 savepbq = parsebackquote;
11523 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011524 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011525 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011526 exception_handler = savehandler;
11527 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011528 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011529 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011530 str = NULL;
11531 savelen = out - (char *)stackblock();
11532 if (savelen > 0) {
11533 str = ckmalloc(savelen);
11534 memcpy(str, stackblock(), savelen);
11535 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011536 savehandler = exception_handler;
11537 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011538 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011539 if (oldstyle) {
11540 /* We must read until the closing backquote, giving special
11541 treatment to some slashes, and then push the string and
11542 reread it as input, interpreting it normally. */
11543 char *pout;
11544 int pc;
11545 size_t psavelen;
11546 char *pstr;
11547
11548
11549 STARTSTACKSTR(pout);
11550 for (;;) {
11551 if (needprompt) {
11552 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011553 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011554 pc = pgetc();
11555 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011556 case '`':
11557 goto done;
11558
11559 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011560 pc = pgetc();
11561 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011562 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011563 if (doprompt)
11564 setprompt(2);
11565 /*
11566 * If eating a newline, avoid putting
11567 * the newline into the new character
11568 * stream (via the STPUTC after the
11569 * switch).
11570 */
11571 continue;
11572 }
11573 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011574 && (!dblquote || pc != '"')
11575 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011576 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011577 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011578 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011579 break;
11580 }
11581 /* fall through */
11582
11583 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011584 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011585 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011586 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011587
11588 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011589 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011590 needprompt = doprompt;
11591 break;
11592
11593 default:
11594 break;
11595 }
11596 STPUTC(pc, pout);
11597 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011598 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011599 STPUTC('\0', pout);
11600 psavelen = pout - (char *)stackblock();
11601 if (psavelen > 0) {
11602 pstr = grabstackstr(pout);
11603 setinputstring(pstr);
11604 }
11605 }
11606 nlpp = &bqlist;
11607 while (*nlpp)
11608 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011609 *nlpp = stzalloc(sizeof(**nlpp));
11610 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011611 parsebackquote = oldstyle;
11612
11613 if (oldstyle) {
11614 saveprompt = doprompt;
11615 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011616 }
11617
Eric Andersenc470f442003-07-28 09:56:35 +000011618 n = list(2);
11619
11620 if (oldstyle)
11621 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011622 else if (readtoken() != TRP)
11623 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011624
11625 (*nlpp)->n = n;
11626 if (oldstyle) {
11627 /*
11628 * Start reading from old file again, ignoring any pushed back
11629 * tokens left from the backquote parsing
11630 */
11631 popfile();
11632 tokpushback = 0;
11633 }
11634 while (stackblocksize() <= savelen)
11635 growstackblock();
11636 STARTSTACKSTR(out);
11637 if (str) {
11638 memcpy(out, str, savelen);
11639 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011640 INT_OFF;
11641 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011642 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011643 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011644 }
11645 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011646 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011647 if (arinest || dblquote)
11648 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11649 else
11650 USTPUTC(CTLBACKQ, out);
11651 if (oldstyle)
11652 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011653 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011654}
11655
Mike Frysinger98c52642009-04-02 10:02:37 +000011656#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011657/*
11658 * Parse an arithmetic expansion (indicate start of one and set state)
11659 */
Eric Andersenc470f442003-07-28 09:56:35 +000011660parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011661 if (++arinest == 1) {
11662 prevsyntax = syntax;
11663 syntax = ARISYNTAX;
11664 USTPUTC(CTLARI, out);
11665 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011666 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011667 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011668 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011669 } else {
11670 /*
11671 * we collapse embedded arithmetic expansion to
11672 * parenthesis, which should be equivalent
11673 */
11674 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011675 }
Eric Andersenc470f442003-07-28 09:56:35 +000011676 goto parsearith_return;
11677}
11678#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011679
Eric Andersenc470f442003-07-28 09:56:35 +000011680} /* end of readtoken */
11681
Eric Andersencb57d552001-06-28 07:25:16 +000011682/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011683 * Read the next input token.
11684 * If the token is a word, we set backquotelist to the list of cmds in
11685 * backquotes. We set quoteflag to true if any part of the word was
11686 * quoted.
11687 * If the token is TREDIR, then we set redirnode to a structure containing
11688 * the redirection.
11689 * In all cases, the variable startlinno is set to the number of the line
11690 * on which the token starts.
11691 *
11692 * [Change comment: here documents and internal procedures]
11693 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11694 * word parsing code into a separate routine. In this case, readtoken
11695 * doesn't need to have any internal procedures, but parseword does.
11696 * We could also make parseoperator in essence the main routine, and
11697 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011698 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011699#define NEW_xxreadtoken
11700#ifdef NEW_xxreadtoken
11701/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011702static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011703 '\n', '(', ')', /* singles */
11704 '&', '|', ';', /* doubles */
11705 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011706};
Eric Andersencb57d552001-06-28 07:25:16 +000011707
Denis Vlasenko834dee72008-10-07 09:18:30 +000011708#define xxreadtoken_singles 3
11709#define xxreadtoken_doubles 3
11710
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011711static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011712 TNL, TLP, TRP, /* only single occurrence allowed */
11713 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11714 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011715 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011716};
11717
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011718static int
11719xxreadtoken(void)
11720{
11721 int c;
11722
11723 if (tokpushback) {
11724 tokpushback = 0;
11725 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011726 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011727 if (needprompt) {
11728 setprompt(2);
11729 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011730 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011731 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011732 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011733 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011734 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011735
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011736 if (c == '#') {
11737 while ((c = pgetc()) != '\n' && c != PEOF)
11738 continue;
11739 pungetc();
11740 } else if (c == '\\') {
11741 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011742 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011743 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011744 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011745 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011746 if (doprompt)
11747 setprompt(2);
11748 } else {
11749 const char *p;
11750
11751 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11752 if (c != PEOF) {
11753 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011754 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011755 needprompt = doprompt;
11756 }
11757
11758 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011759 if (p == NULL)
11760 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011761
Denis Vlasenko834dee72008-10-07 09:18:30 +000011762 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11763 int cc = pgetc();
11764 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011765 p += xxreadtoken_doubles + 1;
11766 } else {
11767 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011768#if ENABLE_ASH_BASH_COMPAT
11769 if (c == '&' && cc == '>') /* &> */
11770 break; /* return readtoken1(...) */
11771#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011772 }
11773 }
11774 }
11775 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11776 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011777 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011778 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011779
11780 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011781}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011782#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011783#define RETURN(token) return lasttoken = token
11784static int
11785xxreadtoken(void)
11786{
11787 int c;
11788
11789 if (tokpushback) {
11790 tokpushback = 0;
11791 return lasttoken;
11792 }
11793 if (needprompt) {
11794 setprompt(2);
11795 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011796 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011797 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011798 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011799 switch (c) {
11800 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011801 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011802 continue;
11803 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011804 while ((c = pgetc()) != '\n' && c != PEOF)
11805 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011806 pungetc();
11807 continue;
11808 case '\\':
11809 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011810 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011811 if (doprompt)
11812 setprompt(2);
11813 continue;
11814 }
11815 pungetc();
11816 goto breakloop;
11817 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011818 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011819 needprompt = doprompt;
11820 RETURN(TNL);
11821 case PEOF:
11822 RETURN(TEOF);
11823 case '&':
11824 if (pgetc() == '&')
11825 RETURN(TAND);
11826 pungetc();
11827 RETURN(TBACKGND);
11828 case '|':
11829 if (pgetc() == '|')
11830 RETURN(TOR);
11831 pungetc();
11832 RETURN(TPIPE);
11833 case ';':
11834 if (pgetc() == ';')
11835 RETURN(TENDCASE);
11836 pungetc();
11837 RETURN(TSEMI);
11838 case '(':
11839 RETURN(TLP);
11840 case ')':
11841 RETURN(TRP);
11842 default:
11843 goto breakloop;
11844 }
11845 }
11846 breakloop:
11847 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11848#undef RETURN
11849}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011850#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011851
11852static int
11853readtoken(void)
11854{
11855 int t;
11856#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011857 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011858#endif
11859
11860#if ENABLE_ASH_ALIAS
11861 top:
11862#endif
11863
11864 t = xxreadtoken();
11865
11866 /*
11867 * eat newlines
11868 */
11869 if (checkkwd & CHKNL) {
11870 while (t == TNL) {
11871 parseheredoc();
11872 t = xxreadtoken();
11873 }
11874 }
11875
11876 if (t != TWORD || quoteflag) {
11877 goto out;
11878 }
11879
11880 /*
11881 * check for keywords
11882 */
11883 if (checkkwd & CHKKWD) {
11884 const char *const *pp;
11885
11886 pp = findkwd(wordtext);
11887 if (pp) {
11888 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011889 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011890 goto out;
11891 }
11892 }
11893
11894 if (checkkwd & CHKALIAS) {
11895#if ENABLE_ASH_ALIAS
11896 struct alias *ap;
11897 ap = lookupalias(wordtext, 1);
11898 if (ap != NULL) {
11899 if (*ap->val) {
11900 pushstring(ap->val, ap);
11901 }
11902 goto top;
11903 }
11904#endif
11905 }
11906 out:
11907 checkkwd = 0;
11908#if DEBUG
11909 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011910 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011911 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011912 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011913#endif
11914 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011915}
11916
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011917static char
11918peektoken(void)
11919{
11920 int t;
11921
11922 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011923 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011924 return tokname_array[t][0];
11925}
Eric Andersencb57d552001-06-28 07:25:16 +000011926
11927/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011928 * Read and parse a command. Returns NODE_EOF on end of file.
11929 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011930 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011931static union node *
11932parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011933{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011934 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011935
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011936 tokpushback = 0;
11937 doprompt = interact;
11938 if (doprompt)
11939 setprompt(doprompt);
11940 needprompt = 0;
11941 t = readtoken();
11942 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011943 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011944 if (t == TNL)
11945 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011946 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011947 return list(1);
11948}
11949
11950/*
11951 * Input any here documents.
11952 */
11953static void
11954parseheredoc(void)
11955{
11956 struct heredoc *here;
11957 union node *n;
11958
11959 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011960 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011961
11962 while (here) {
11963 if (needprompt) {
11964 setprompt(2);
11965 }
11966 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11967 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011968 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011969 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011970 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011971 n->narg.text = wordtext;
11972 n->narg.backquote = backquotelist;
11973 here->here->nhere.doc = n;
11974 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011975 }
Eric Andersencb57d552001-06-28 07:25:16 +000011976}
11977
11978
11979/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011980 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011981 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011982#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011983static const char *
11984expandstr(const char *ps)
11985{
11986 union node n;
11987
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011988 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11989 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011990 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011991 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011992 popfile();
11993
11994 n.narg.type = NARG;
11995 n.narg.next = NULL;
11996 n.narg.text = wordtext;
11997 n.narg.backquote = backquotelist;
11998
11999 expandarg(&n, NULL, 0);
12000 return stackblock();
12001}
12002#endif
12003
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012004/*
12005 * Execute a command or commands contained in a string.
12006 */
12007static int
12008evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012009{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012010 union node *n;
12011 struct stackmark smark;
12012 int skip;
12013
12014 setinputstring(s);
12015 setstackmark(&smark);
12016
12017 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012018 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012019 evaltree(n, 0);
12020 popstackmark(&smark);
12021 skip = evalskip;
12022 if (skip)
12023 break;
12024 }
12025 popfile();
12026
12027 skip &= mask;
12028 evalskip = skip;
12029 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012030}
12031
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012032/*
12033 * The eval command.
12034 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012035static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012036evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012037{
12038 char *p;
12039 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012040
Denis Vlasenko68404f12008-03-17 09:00:54 +000012041 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012042 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012043 argv += 2;
12044 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012045 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012046 for (;;) {
12047 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012048 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012049 if (p == NULL)
12050 break;
12051 STPUTC(' ', concat);
12052 }
12053 STPUTC('\0', concat);
12054 p = grabstackstr(concat);
12055 }
12056 evalstring(p, ~SKIPEVAL);
12057
12058 }
12059 return exitstatus;
12060}
12061
12062/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012063 * Read and execute commands.
12064 * "Top" is nonzero for the top level command loop;
12065 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012066 */
12067static int
12068cmdloop(int top)
12069{
12070 union node *n;
12071 struct stackmark smark;
12072 int inter;
12073 int numeof = 0;
12074
12075 TRACE(("cmdloop(%d) called\n", top));
12076 for (;;) {
12077 int skip;
12078
12079 setstackmark(&smark);
12080#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012081 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012082 showjobs(stderr, SHOW_CHANGED);
12083#endif
12084 inter = 0;
12085 if (iflag && top) {
12086 inter++;
12087#if ENABLE_ASH_MAIL
12088 chkmail();
12089#endif
12090 }
12091 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012092#if DEBUG
12093 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012094 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012095#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012096 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012097 if (!top || numeof >= 50)
12098 break;
12099 if (!stoppedjobs()) {
12100 if (!Iflag)
12101 break;
12102 out2str("\nUse \"exit\" to leave shell.\n");
12103 }
12104 numeof++;
12105 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012106 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12107 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012108 numeof = 0;
12109 evaltree(n, 0);
12110 }
12111 popstackmark(&smark);
12112 skip = evalskip;
12113
12114 if (skip) {
12115 evalskip = 0;
12116 return skip & SKIPEVAL;
12117 }
12118 }
12119 return 0;
12120}
12121
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012122/*
12123 * Take commands from a file. To be compatible we should do a path
12124 * search for the file, which is necessary to find sub-commands.
12125 */
12126static char *
12127find_dot_file(char *name)
12128{
12129 char *fullname;
12130 const char *path = pathval();
12131 struct stat statb;
12132
12133 /* don't try this for absolute or relative paths */
12134 if (strchr(name, '/'))
12135 return name;
12136
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012137 /* IIRC standards do not say whether . is to be searched.
12138 * And it is even smaller this way, making it unconditional for now:
12139 */
12140 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12141 fullname = name;
12142 goto try_cur_dir;
12143 }
12144
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012145 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012146 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012147 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12148 /*
12149 * Don't bother freeing here, since it will
12150 * be freed by the caller.
12151 */
12152 return fullname;
12153 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012154 if (fullname != name)
12155 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012156 }
12157
12158 /* not found in the PATH */
12159 ash_msg_and_raise_error("%s: not found", name);
12160 /* NOTREACHED */
12161}
12162
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012163static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012164dotcmd(int argc, char **argv)
12165{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012166 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012167 struct strlist *sp;
12168 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012169
12170 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012171 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012172
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012173 if (!argv[1]) {
12174 /* bash says: "bash: .: filename argument required" */
12175 return 2; /* bash compat */
12176 }
12177
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012178 /* "false; . empty_file; echo $?" should print 0, not 1: */
12179 exitstatus = 0;
12180
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012181 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012182
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012183 argv += 2;
12184 argc -= 2;
12185 if (argc) { /* argc > 0, argv[0] != NULL */
12186 saveparam = shellparam;
12187 shellparam.malloced = 0;
12188 shellparam.nparam = argc;
12189 shellparam.p = argv;
12190 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012191
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012192 setinputfile(fullname, INPUT_PUSH_FILE);
12193 commandname = fullname;
12194 cmdloop(0);
12195 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012196
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012197 if (argc) {
12198 freeparam(&shellparam);
12199 shellparam = saveparam;
12200 };
12201
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012202 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012203}
12204
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012205static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012206exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012207{
12208 if (stoppedjobs())
12209 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012210 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012211 exitstatus = number(argv[1]);
12212 raise_exception(EXEXIT);
12213 /* NOTREACHED */
12214}
12215
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012216/*
12217 * Read a file containing shell functions.
12218 */
12219static void
12220readcmdfile(char *name)
12221{
12222 setinputfile(name, INPUT_PUSH_FILE);
12223 cmdloop(0);
12224 popfile();
12225}
12226
12227
Denis Vlasenkocc571512007-02-23 21:10:35 +000012228/* ============ find_command inplementation */
12229
12230/*
12231 * Resolve a command name. If you change this routine, you may have to
12232 * change the shellexec routine as well.
12233 */
12234static void
12235find_command(char *name, struct cmdentry *entry, int act, const char *path)
12236{
12237 struct tblentry *cmdp;
12238 int idx;
12239 int prev;
12240 char *fullname;
12241 struct stat statb;
12242 int e;
12243 int updatetbl;
12244 struct builtincmd *bcmd;
12245
12246 /* If name contains a slash, don't use PATH or hash table */
12247 if (strchr(name, '/') != NULL) {
12248 entry->u.index = -1;
12249 if (act & DO_ABS) {
12250 while (stat(name, &statb) < 0) {
12251#ifdef SYSV
12252 if (errno == EINTR)
12253 continue;
12254#endif
12255 entry->cmdtype = CMDUNKNOWN;
12256 return;
12257 }
12258 }
12259 entry->cmdtype = CMDNORMAL;
12260 return;
12261 }
12262
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012263/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012264
12265 updatetbl = (path == pathval());
12266 if (!updatetbl) {
12267 act |= DO_ALTPATH;
12268 if (strstr(path, "%builtin") != NULL)
12269 act |= DO_ALTBLTIN;
12270 }
12271
12272 /* If name is in the table, check answer will be ok */
12273 cmdp = cmdlookup(name, 0);
12274 if (cmdp != NULL) {
12275 int bit;
12276
12277 switch (cmdp->cmdtype) {
12278 default:
12279#if DEBUG
12280 abort();
12281#endif
12282 case CMDNORMAL:
12283 bit = DO_ALTPATH;
12284 break;
12285 case CMDFUNCTION:
12286 bit = DO_NOFUNC;
12287 break;
12288 case CMDBUILTIN:
12289 bit = DO_ALTBLTIN;
12290 break;
12291 }
12292 if (act & bit) {
12293 updatetbl = 0;
12294 cmdp = NULL;
12295 } else if (cmdp->rehash == 0)
12296 /* if not invalidated by cd, we're done */
12297 goto success;
12298 }
12299
12300 /* If %builtin not in path, check for builtin next */
12301 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012302 if (bcmd) {
12303 if (IS_BUILTIN_REGULAR(bcmd))
12304 goto builtin_success;
12305 if (act & DO_ALTPATH) {
12306 if (!(act & DO_ALTBLTIN))
12307 goto builtin_success;
12308 } else if (builtinloc <= 0) {
12309 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012310 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012311 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012312
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012313#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012314 {
12315 int applet_no = find_applet_by_name(name);
12316 if (applet_no >= 0) {
12317 entry->cmdtype = CMDNORMAL;
12318 entry->u.index = -2 - applet_no;
12319 return;
12320 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012321 }
12322#endif
12323
Denis Vlasenkocc571512007-02-23 21:10:35 +000012324 /* We have to search path. */
12325 prev = -1; /* where to start */
12326 if (cmdp && cmdp->rehash) { /* doing a rehash */
12327 if (cmdp->cmdtype == CMDBUILTIN)
12328 prev = builtinloc;
12329 else
12330 prev = cmdp->param.index;
12331 }
12332
12333 e = ENOENT;
12334 idx = -1;
12335 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012336 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012337 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012338 /* NB: code below will still use fullname
12339 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012340 idx++;
12341 if (pathopt) {
12342 if (prefix(pathopt, "builtin")) {
12343 if (bcmd)
12344 goto builtin_success;
12345 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012346 }
12347 if ((act & DO_NOFUNC)
12348 || !prefix(pathopt, "func")
12349 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012350 continue;
12351 }
12352 }
12353 /* if rehash, don't redo absolute path names */
12354 if (fullname[0] == '/' && idx <= prev) {
12355 if (idx < prev)
12356 continue;
12357 TRACE(("searchexec \"%s\": no change\n", name));
12358 goto success;
12359 }
12360 while (stat(fullname, &statb) < 0) {
12361#ifdef SYSV
12362 if (errno == EINTR)
12363 continue;
12364#endif
12365 if (errno != ENOENT && errno != ENOTDIR)
12366 e = errno;
12367 goto loop;
12368 }
12369 e = EACCES; /* if we fail, this will be the error */
12370 if (!S_ISREG(statb.st_mode))
12371 continue;
12372 if (pathopt) { /* this is a %func directory */
12373 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012374 /* NB: stalloc will return space pointed by fullname
12375 * (because we don't have any intervening allocations
12376 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012377 readcmdfile(fullname);
12378 cmdp = cmdlookup(name, 0);
12379 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12380 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12381 stunalloc(fullname);
12382 goto success;
12383 }
12384 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12385 if (!updatetbl) {
12386 entry->cmdtype = CMDNORMAL;
12387 entry->u.index = idx;
12388 return;
12389 }
12390 INT_OFF;
12391 cmdp = cmdlookup(name, 1);
12392 cmdp->cmdtype = CMDNORMAL;
12393 cmdp->param.index = idx;
12394 INT_ON;
12395 goto success;
12396 }
12397
12398 /* We failed. If there was an entry for this command, delete it */
12399 if (cmdp && updatetbl)
12400 delete_cmd_entry();
12401 if (act & DO_ERR)
12402 ash_msg("%s: %s", name, errmsg(e, "not found"));
12403 entry->cmdtype = CMDUNKNOWN;
12404 return;
12405
12406 builtin_success:
12407 if (!updatetbl) {
12408 entry->cmdtype = CMDBUILTIN;
12409 entry->u.cmd = bcmd;
12410 return;
12411 }
12412 INT_OFF;
12413 cmdp = cmdlookup(name, 1);
12414 cmdp->cmdtype = CMDBUILTIN;
12415 cmdp->param.cmd = bcmd;
12416 INT_ON;
12417 success:
12418 cmdp->rehash = 0;
12419 entry->cmdtype = cmdp->cmdtype;
12420 entry->u = cmdp->param;
12421}
12422
12423
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012424/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012425
Eric Andersencb57d552001-06-28 07:25:16 +000012426/*
Eric Andersencb57d552001-06-28 07:25:16 +000012427 * The trap builtin.
12428 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012429static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012430trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012431{
12432 char *action;
12433 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012434 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012435
Eric Andersenc470f442003-07-28 09:56:35 +000012436 nextopt(nullstr);
12437 ap = argptr;
12438 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012439 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012440 char *tr = trap_ptr[signo];
12441 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012442 /* note: bash adds "SIG", but only if invoked
12443 * as "bash". If called as "sh", or if set -o posix,
12444 * then it prints short signal names.
12445 * We are printing short names: */
12446 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012447 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012448 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012449 /* trap_ptr != trap only if we are in special-cased `trap` code.
12450 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012451 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012452 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012453 }
12454 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012455 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012456 if (trap_ptr != trap) {
12457 free(trap_ptr);
12458 trap_ptr = trap;
12459 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012460 */
Eric Andersencb57d552001-06-28 07:25:16 +000012461 return 0;
12462 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012463
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012464 action = NULL;
12465 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012466 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012467 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012468 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012469 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012470 if (signo < 0) {
12471 /* Mimic bash message exactly */
12472 ash_msg("%s: invalid signal specification", *ap);
12473 exitcode = 1;
12474 goto next;
12475 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012476 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012477 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012478 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012479 action = NULL;
12480 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012481 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012482 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012483 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012484 if (action)
12485 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012486 trap[signo] = action;
12487 if (signo != 0)
12488 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012489 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012490 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012491 ap++;
12492 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012493 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012494}
12495
Eric Andersenc470f442003-07-28 09:56:35 +000012496
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012497/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012498
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012499#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012500/*
12501 * Lists available builtins
12502 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012503static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012504helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012505{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012506 unsigned col;
12507 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012508
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012509 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012510 "Built-in commands:\n"
12511 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012512 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012513 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012514 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012515 if (col > 60) {
12516 out1fmt("\n");
12517 col = 0;
12518 }
12519 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012520#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012521 {
12522 const char *a = applet_names;
12523 while (*a) {
12524 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12525 if (col > 60) {
12526 out1fmt("\n");
12527 col = 0;
12528 }
12529 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012530 }
12531 }
12532#endif
12533 out1fmt("\n\n");
12534 return EXIT_SUCCESS;
12535}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012536#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012537
Eric Andersencb57d552001-06-28 07:25:16 +000012538/*
Eric Andersencb57d552001-06-28 07:25:16 +000012539 * The export and readonly commands.
12540 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012541static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012542exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012543{
12544 struct var *vp;
12545 char *name;
12546 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012547 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012548 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012549
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012550 if (nextopt("p") != 'p') {
12551 aptr = argptr;
12552 name = *aptr;
12553 if (name) {
12554 do {
12555 p = strchr(name, '=');
12556 if (p != NULL) {
12557 p++;
12558 } else {
12559 vp = *findvar(hashvar(name), name);
12560 if (vp) {
12561 vp->flags |= flag;
12562 continue;
12563 }
Eric Andersencb57d552001-06-28 07:25:16 +000012564 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012565 setvar(name, p, flag);
12566 } while ((name = *++aptr) != NULL);
12567 return 0;
12568 }
Eric Andersencb57d552001-06-28 07:25:16 +000012569 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012570 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012571 return 0;
12572}
12573
Eric Andersencb57d552001-06-28 07:25:16 +000012574/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012575 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012576 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012577static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012578unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012579{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012580 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012581
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012582 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012583 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012584 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012585}
12586
Eric Andersencb57d552001-06-28 07:25:16 +000012587/*
Eric Andersencb57d552001-06-28 07:25:16 +000012588 * The unset builtin command. We unset the function before we unset the
12589 * variable to allow a function to be unset when there is a readonly variable
12590 * with the same name.
12591 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012592static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012593unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012594{
12595 char **ap;
12596 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012597 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012598 int ret = 0;
12599
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012600 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012601 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012602 }
Eric Andersencb57d552001-06-28 07:25:16 +000012603
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012604 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012605 if (flag != 'f') {
12606 i = unsetvar(*ap);
12607 ret |= i;
12608 if (!(i & 2))
12609 continue;
12610 }
12611 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012612 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012613 }
Eric Andersenc470f442003-07-28 09:56:35 +000012614 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012615}
12616
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012617static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012618 ' ', offsetof(struct tms, tms_utime),
12619 '\n', offsetof(struct tms, tms_stime),
12620 ' ', offsetof(struct tms, tms_cutime),
12621 '\n', offsetof(struct tms, tms_cstime),
12622 0
12623};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012624static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012625timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012626{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012627 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012628 const unsigned char *p;
12629 struct tms buf;
12630
12631 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012632 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012633
12634 p = timescmd_str;
12635 do {
12636 t = *(clock_t *)(((char *) &buf) + p[1]);
12637 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012638 t = t % clk_tck;
12639 out1fmt("%lum%lu.%03lus%c",
12640 s / 60, s % 60,
12641 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012642 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012643 p += 2;
12644 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012645
Eric Andersencb57d552001-06-28 07:25:16 +000012646 return 0;
12647}
12648
Mike Frysinger98c52642009-04-02 10:02:37 +000012649#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012650/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012651 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012652 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012653 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012654 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012655 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012656static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012657letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012658{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012659 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012660
Denis Vlasenko68404f12008-03-17 09:00:54 +000012661 argv++;
12662 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012663 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012664 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012665 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012666 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012667
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012668 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012669}
Eric Andersenc470f442003-07-28 09:56:35 +000012670#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012671
Eric Andersenc470f442003-07-28 09:56:35 +000012672/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012673 * The read builtin. Options:
12674 * -r Do not interpret '\' specially
12675 * -s Turn off echo (tty only)
12676 * -n NCHARS Read NCHARS max
12677 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12678 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12679 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012680 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012681 * TODO: bash also has:
12682 * -a ARRAY Read into array[0],[1],etc
12683 * -d DELIM End on DELIM char, not newline
12684 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012685 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012686static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012687readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012688{
Denys Vlasenko73067272010-01-12 22:11:24 +010012689 char *opt_n = NULL;
12690 char *opt_p = NULL;
12691 char *opt_t = NULL;
12692 char *opt_u = NULL;
12693 int read_flags = 0;
12694 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012695 int i;
12696
Denys Vlasenko73067272010-01-12 22:11:24 +010012697 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012698 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012699 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012700 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012701 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012702 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012703 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012704 break;
12705 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012706 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012707 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012708 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012709 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012710 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012711 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012712 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012713 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012714 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012715 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012716 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012717 default:
12718 break;
12719 }
Eric Andersenc470f442003-07-28 09:56:35 +000012720 }
Paul Fox02eb9342005-09-07 16:56:02 +000012721
Denys Vlasenko03dad222010-01-12 23:29:57 +010012722 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012723 argptr,
12724 bltinlookup("IFS"), /* can be NULL */
12725 read_flags,
12726 opt_n,
12727 opt_p,
12728 opt_t,
12729 opt_u
12730 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012731
Denys Vlasenko73067272010-01-12 22:11:24 +010012732 if ((uintptr_t)r > 1)
12733 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012734
Denys Vlasenko73067272010-01-12 22:11:24 +010012735 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012736}
12737
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012738static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012739umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012740{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012741 static const char permuser[3] ALIGN1 = "ugo";
12742 static const char permmode[3] ALIGN1 = "rwx";
12743 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012744 S_IRUSR, S_IWUSR, S_IXUSR,
12745 S_IRGRP, S_IWGRP, S_IXGRP,
12746 S_IROTH, S_IWOTH, S_IXOTH
12747 };
12748
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012749 /* TODO: use bb_parse_mode() instead */
12750
Eric Andersenc470f442003-07-28 09:56:35 +000012751 char *ap;
12752 mode_t mask;
12753 int i;
12754 int symbolic_mode = 0;
12755
12756 while (nextopt("S") != '\0') {
12757 symbolic_mode = 1;
12758 }
12759
Denis Vlasenkob012b102007-02-19 22:43:01 +000012760 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012761 mask = umask(0);
12762 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012763 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012764
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012765 ap = *argptr;
12766 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012767 if (symbolic_mode) {
12768 char buf[18];
12769 char *p = buf;
12770
12771 for (i = 0; i < 3; i++) {
12772 int j;
12773
12774 *p++ = permuser[i];
12775 *p++ = '=';
12776 for (j = 0; j < 3; j++) {
12777 if ((mask & permmask[3 * i + j]) == 0) {
12778 *p++ = permmode[j];
12779 }
12780 }
12781 *p++ = ',';
12782 }
12783 *--p = 0;
12784 puts(buf);
12785 } else {
12786 out1fmt("%.4o\n", mask);
12787 }
12788 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012789 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012790 mask = 0;
12791 do {
12792 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012793 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012794 mask = (mask << 3) + (*ap - '0');
12795 } while (*++ap != '\0');
12796 umask(mask);
12797 } else {
12798 mask = ~mask & 0777;
12799 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012800 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012801 }
12802 umask(~mask & 0777);
12803 }
12804 }
12805 return 0;
12806}
12807
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012808static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012809ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012810{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012811 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012812}
12813
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012814/* ============ main() and helpers */
12815
12816/*
12817 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012818 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012819static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012820static void
12821exitshell(void)
12822{
12823 struct jmploc loc;
12824 char *p;
12825 int status;
12826
12827 status = exitstatus;
12828 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12829 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012830 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012831/* dash bug: it just does _exit(exitstatus) here
12832 * but we have to do setjobctl(0) first!
12833 * (bug is still not fixed in dash-0.5.3 - if you run dash
12834 * under Midnight Commander, on exit from dash MC is backgrounded) */
12835 status = exitstatus;
12836 goto out;
12837 }
12838 exception_handler = &loc;
12839 p = trap[0];
12840 if (p) {
12841 trap[0] = NULL;
12842 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012843 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012844 }
12845 flush_stdout_stderr();
12846 out:
12847 setjobctl(0);
12848 _exit(status);
12849 /* NOTREACHED */
12850}
12851
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012852static void
12853init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012854{
12855 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012856 /* we will never free this */
12857 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012858
12859 /* from trap.c: */
12860 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012861 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12862 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12863 */
12864 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012865
12866 /* from var.c: */
12867 {
12868 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012869 const char *p;
12870 struct stat st1, st2;
12871
12872 initvar();
12873 for (envp = environ; envp && *envp; envp++) {
12874 if (strchr(*envp, '=')) {
12875 setvareq(*envp, VEXPORT|VTEXTFIXED);
12876 }
12877 }
12878
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012879 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012880
12881 p = lookupvar("PWD");
12882 if (p)
12883 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12884 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12885 p = '\0';
12886 setpwd(p, 0);
12887 }
12888}
12889
12890/*
12891 * Process the shell command line arguments.
12892 */
12893static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012894procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012895{
12896 int i;
12897 const char *xminusc;
12898 char **xargv;
12899
12900 xargv = argv;
12901 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012902 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012903 xargv++;
12904 for (i = 0; i < NOPTS; i++)
12905 optlist[i] = 2;
12906 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012907 if (options(1)) {
12908 /* it already printed err message */
12909 raise_exception(EXERROR);
12910 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012911 xargv = argptr;
12912 xminusc = minusc;
12913 if (*xargv == NULL) {
12914 if (xminusc)
12915 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12916 sflag = 1;
12917 }
12918 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12919 iflag = 1;
12920 if (mflag == 2)
12921 mflag = iflag;
12922 for (i = 0; i < NOPTS; i++)
12923 if (optlist[i] == 2)
12924 optlist[i] = 0;
12925#if DEBUG == 2
12926 debug = 1;
12927#endif
12928 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12929 if (xminusc) {
12930 minusc = *xargv++;
12931 if (*xargv)
12932 goto setarg0;
12933 } else if (!sflag) {
12934 setinputfile(*xargv, 0);
12935 setarg0:
12936 arg0 = *xargv++;
12937 commandname = arg0;
12938 }
12939
12940 shellparam.p = xargv;
12941#if ENABLE_ASH_GETOPTS
12942 shellparam.optind = 1;
12943 shellparam.optoff = -1;
12944#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012945 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012946 while (*xargv) {
12947 shellparam.nparam++;
12948 xargv++;
12949 }
12950 optschanged();
12951}
12952
12953/*
12954 * Read /etc/profile or .profile.
12955 */
12956static void
12957read_profile(const char *name)
12958{
12959 int skip;
12960
12961 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12962 return;
12963 skip = cmdloop(0);
12964 popfile();
12965 if (skip)
12966 exitshell();
12967}
12968
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012969/*
12970 * This routine is called when an error or an interrupt occurs in an
12971 * interactive shell and control is returned to the main command loop.
12972 */
12973static void
12974reset(void)
12975{
12976 /* from eval.c: */
12977 evalskip = 0;
12978 loopnest = 0;
12979 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000012980 g_parsefile->left_in_buffer = 0;
12981 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012982 popallfiles();
12983 /* from parser.c: */
12984 tokpushback = 0;
12985 checkkwd = 0;
12986 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000012987 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012988}
12989
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012990#if PROFILE
12991static short profile_buf[16384];
12992extern int etext();
12993#endif
12994
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012995/*
12996 * Main routine. We initialize things, parse the arguments, execute
12997 * profiles if we're a login shell, and then call cmdloop to execute
12998 * commands. The setjmp call sets up the location to jump to when an
12999 * exception occurs. When an exception occurs the variable "state"
13000 * is used to figure out how far we had gotten.
13001 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013002int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013003int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013004{
Mike Frysinger98c52642009-04-02 10:02:37 +000013005 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013006 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013007 struct jmploc jmploc;
13008 struct stackmark smark;
13009
Denis Vlasenko01631112007-12-16 17:20:38 +000013010 /* Initialize global data */
13011 INIT_G_misc();
13012 INIT_G_memstack();
13013 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013014#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013015 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013016#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013017 INIT_G_cmdtable();
13018
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013019#if PROFILE
13020 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13021#endif
13022
13023#if ENABLE_FEATURE_EDITING
13024 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13025#endif
13026 state = 0;
13027 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013028 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013029 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013030
13031 reset();
13032
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013033 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013034 if (e == EXERROR)
13035 exitstatus = 2;
13036 s = state;
13037 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13038 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013039 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013040 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013041
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013042 popstackmark(&smark);
13043 FORCE_INT_ON; /* enable interrupts */
13044 if (s == 1)
13045 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013046 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013047 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013048 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013049 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013050 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013051 }
13052 exception_handler = &jmploc;
13053#if DEBUG
13054 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013055 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013056 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013057#endif
13058 rootpid = getpid();
13059
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013060 init();
13061 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013062 procargs(argv);
13063
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013064#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13065 if (iflag) {
13066 const char *hp = lookupvar("HISTFILE");
13067
13068 if (hp == NULL) {
13069 hp = lookupvar("HOME");
13070 if (hp != NULL) {
13071 char *defhp = concat_path_file(hp, ".ash_history");
13072 setvar("HISTFILE", defhp, 0);
13073 free(defhp);
13074 }
13075 }
13076 }
13077#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013078 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013079 isloginsh = 1;
13080 if (isloginsh) {
13081 state = 1;
13082 read_profile("/etc/profile");
13083 state1:
13084 state = 2;
13085 read_profile(".profile");
13086 }
13087 state2:
13088 state = 3;
13089 if (
13090#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013091 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013092#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013093 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013094 ) {
13095 shinit = lookupvar("ENV");
13096 if (shinit != NULL && *shinit != '\0') {
13097 read_profile(shinit);
13098 }
13099 }
13100 state3:
13101 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013102 if (minusc) {
13103 /* evalstring pushes parsefile stack.
13104 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013105 * is one of stacked source fds.
13106 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013107 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013108 // ^^ not necessary since now we special-case fd 0
13109 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013110 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013111 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013112
13113 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013114#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013115 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013116 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013117 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013118 line_input_state->hist_file = hp;
13119 }
13120#endif
13121 state4: /* XXX ??? - why isn't this before the "if" statement */
13122 cmdloop(1);
13123 }
13124#if PROFILE
13125 monitor(0);
13126#endif
13127#ifdef GPROF
13128 {
13129 extern void _mcleanup(void);
13130 _mcleanup();
13131 }
13132#endif
13133 exitshell();
13134 /* NOTREACHED */
13135}
13136
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013137
Eric Andersendf82f612001-06-28 07:46:40 +000013138/*-
13139 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013140 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013141 *
13142 * This code is derived from software contributed to Berkeley by
13143 * Kenneth Almquist.
13144 *
13145 * Redistribution and use in source and binary forms, with or without
13146 * modification, are permitted provided that the following conditions
13147 * are met:
13148 * 1. Redistributions of source code must retain the above copyright
13149 * notice, this list of conditions and the following disclaimer.
13150 * 2. Redistributions in binary form must reproduce the above copyright
13151 * notice, this list of conditions and the following disclaimer in the
13152 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013153 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013154 * may be used to endorse or promote products derived from this software
13155 * without specific prior written permission.
13156 *
13157 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13158 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13159 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13160 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13161 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13162 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13163 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13164 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13165 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13166 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13167 * SUCH DAMAGE.
13168 */