blob: 76bf39ec24eefc036db4deebb9dc8f69bf9ccbd8 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
Denys Vlasenko67e15292020-06-24 13:39:13 +020018//config:config SHELL_ASH
19//config: bool #hidden option
20//config: depends on !NOMMU
21//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +020022//config:config ASH
Denys Vlasenkob097a842018-12-28 03:20:17 +010023//config: bool "ash (78 kb)"
Denys Vlasenko771f1992010-07-16 14:31:34 +020024//config: default y
25//config: depends on !NOMMU
Denys Vlasenko67e15292020-06-24 13:39:13 +020026//config: select SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +020027//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020028//config: The most complete and most pedantically correct shell included with
29//config: busybox. This shell is actually a derivative of the Debian 'dash'
30//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
31//config: (written by Kenneth Almquist) from NetBSD.
Denys Vlasenko771f1992010-07-16 14:31:34 +020032//config:
Kang-Che Sung6cd02942017-01-06 17:02:03 +010033//config:# ash options
34//config:# note: Don't remove !NOMMU part in the next line; it would break
35//config:# menuconfig's indenting.
Denys Vlasenko67e15292020-06-24 13:39:13 +020036//config:if !NOMMU && (SHELL_ASH || ASH || SH_IS_ASH || BASH_IS_ASH)
Kang-Che Sung6cd02942017-01-06 17:02:03 +010037//config:
Denys Vlasenko514b51d2016-10-01 14:33:08 +020038//config:config ASH_OPTIMIZE_FOR_SIZE
39//config: bool "Optimize for size instead of speed"
40//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020041//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020042//config:
43//config:config ASH_INTERNAL_GLOB
44//config: bool "Use internal glob() implementation"
Denys Vlasenko326edc32016-12-22 14:36:49 +010045//config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now
Denys Vlasenko67e15292020-06-24 13:39:13 +020046//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020047//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020048//config: Do not use glob() function from libc, use internal implementation.
49//config: Use this if you are getting "glob.h: No such file or directory"
50//config: or similar build errors.
51//config: Note that as of now (2017-01), uclibc and musl glob() both have bugs
52//config: which would break ash if you select N here.
Denys Vlasenkof5604222017-01-10 14:58:54 +010053//config:
54//config:config ASH_BASH_COMPAT
55//config: bool "bash-compatible extensions"
56//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020057//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010058//config:
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010059//config:config ASH_BASH_SOURCE_CURDIR
60//config: bool "'source' and '.' builtins search current directory after $PATH"
61//config: default n # do not encourage non-standard behavior
Denys Vlasenko54c21112018-01-27 20:46:45 +010062//config: depends on ASH_BASH_COMPAT
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010063//config: help
64//config: This is not compliant with standards. Avoid if possible.
65//config:
William Pitcockd8fd88a2018-01-24 18:33:18 +010066//config:config ASH_BASH_NOT_FOUND_HOOK
67//config: bool "command_not_found_handle hook support"
68//config: default y
Denys Vlasenko54c21112018-01-27 20:46:45 +010069//config: depends on ASH_BASH_COMPAT
William Pitcockd8fd88a2018-01-24 18:33:18 +010070//config: help
71//config: Enable support for the 'command_not_found_handle' hook function,
72//config: from GNU bash, which allows for alternative command not found
73//config: handling.
74//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +010075//config:config ASH_JOB_CONTROL
76//config: bool "Job control"
77//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020078//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010079//config:
80//config:config ASH_ALIAS
81//config: bool "Alias support"
82//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020083//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020084//config:
85//config:config ASH_RANDOM_SUPPORT
86//config: bool "Pseudorandom generator and $RANDOM variable"
87//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020088//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020089//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020090//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
91//config: Each read of "$RANDOM" will generate a new pseudorandom value.
92//config: You can reset the generator by using a specified start value.
93//config: After "unset RANDOM" the generator will switch off and this
94//config: variable will no longer have special treatment.
Denys Vlasenko514b51d2016-10-01 14:33:08 +020095//config:
96//config:config ASH_EXPAND_PRMT
97//config: bool "Expand prompt string"
98//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020099//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200100//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200101//config: $PS# may contain volatile content, such as backquote commands.
102//config: This option recreates the prompt string from the environment
103//config: variable each time it is displayed.
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200104//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +0100105//config:config ASH_IDLE_TIMEOUT
Denys Vlasenkof5604222017-01-10 14:58:54 +0100106//config: bool "Idle timeout variable $TMOUT"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200107//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200108//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200109//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200110//config: Enable bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200111//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +0100112//config:config ASH_MAIL
113//config: bool "Check for new mail in interactive shell"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200114//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200115//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200116//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200117//config: Enable "check for new mail" function:
118//config: if set, $MAIL file and $MAILPATH list of files
119//config: are checked for mtime changes, and "you have mail"
120//config: message is printed if change is detected.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200121//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100122//config:config ASH_ECHO
Denys Vlasenkof5604222017-01-10 14:58:54 +0100123//config: bool "echo builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200124//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200125//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200126//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100127//config:config ASH_PRINTF
Denys Vlasenkof5604222017-01-10 14:58:54 +0100128//config: bool "printf builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200129//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200130//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200131//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100132//config:config ASH_TEST
Denys Vlasenkof5604222017-01-10 14:58:54 +0100133//config: bool "test builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200135//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200136//config:
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200137//config:config ASH_HELP
138//config: bool "help builtin"
139//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200140//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +0100141//config:
142//config:config ASH_GETOPTS
143//config: bool "getopts builtin"
144//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200145//config: depends on SHELL_ASH
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200146//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200147//config:config ASH_CMDCMD
Denys Vlasenkof5604222017-01-10 14:58:54 +0100148//config: bool "command builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200149//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200150//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200151//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200152//config: Enable support for the 'command' builtin, which allows
153//config: you to run the specified command or builtin,
154//config: even when there is a function with the same name.
Kang-Che Sung6cd02942017-01-06 17:02:03 +0100155//config:
156//config:endif # ash options
Denys Vlasenko771f1992010-07-16 14:31:34 +0200157
Denys Vlasenko20704f02011-03-23 17:59:27 +0100158//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
Denys Vlasenko205d48e2017-01-29 14:57:33 +0100159// APPLET_ODDNAME:name main location suid_type help
160//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko0b883582016-12-23 16:49:07 +0100161//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko20704f02011-03-23 17:59:27 +0100162
Denys Vlasenko67e15292020-06-24 13:39:13 +0200163//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko20704f02011-03-23 17:59:27 +0100164//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
165
Denys Vlasenko67047462016-12-22 15:21:58 +0100166/*
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100167 * DEBUG=1 to compile in debugging ('set -o debug' turns on)
168 * DEBUG=2 to compile in and turn on debugging.
169 * When debugging is on ("set -o debug" was executed, or DEBUG=2),
170 * debugging info is written to ./trace, quit signal generates core dump.
Denys Vlasenko67047462016-12-22 15:21:58 +0100171 */
172#define DEBUG 0
173/* Tweak debug output verbosity here */
174#define DEBUG_TIME 0
175#define DEBUG_PID 1
176#define DEBUG_SIG 1
177#define DEBUG_INTONOFF 0
178
179#define PROFILE 0
180
181#define JOBS ENABLE_ASH_JOB_CONTROL
182
Denys Vlasenko67047462016-12-22 15:21:58 +0100183#include <fnmatch.h>
184#include <sys/times.h>
185#include <sys/utsname.h> /* for setting $HOSTNAME */
Denys Vlasenko67047462016-12-22 15:21:58 +0100186#include "busybox.h" /* for applet_names */
Ron Yorston71df2d32018-11-27 14:34:25 +0000187#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +0100188# include "embedded_scripts.h"
189#else
190# define NUM_SCRIPTS 0
191#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100192
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100193/* So far, all bash compat is controlled by one config option */
194/* Separate defines document which part of code implements what */
195/* function keyword */
196#define BASH_FUNCTION ENABLE_ASH_BASH_COMPAT
197#define IF_BASH_FUNCTION IF_ASH_BASH_COMPAT
198/* &>file */
199#define BASH_REDIR_OUTPUT ENABLE_ASH_BASH_COMPAT
200#define IF_BASH_REDIR_OUTPUT IF_ASH_BASH_COMPAT
201/* $'...' */
202#define BASH_DOLLAR_SQUOTE ENABLE_ASH_BASH_COMPAT
203#define IF_BASH_DOLLAR_SQUOTE IF_ASH_BASH_COMPAT
204#define BASH_PATTERN_SUBST ENABLE_ASH_BASH_COMPAT
205#define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT
206#define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT
207#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200208/* BASH_TEST2: [[ EXPR ]]
209 * Status of [[ support:
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100210 * && and || work as they should
211 * = is glob match operator, not equality operator: STR = GLOB
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100212 * == same as =
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100213 * =~ is regex match operator: STR =~ REGEX
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200214 * TODO:
215 * singleword+noglob expansion:
216 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
Denys Vlasenko89e9d552018-04-11 01:15:33 +0200217 * [[ /bin/n* ]]; echo 0:$?
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200218 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100219 * ( ) < > should not have special meaning (IOW: should not require quoting)
220 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200221 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100222#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
223#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
224#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
225#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
Ron Yorston1d371862019-04-15 10:52:05 +0100226#define BASH_EPOCH_VARS ENABLE_ASH_BASH_COMPAT
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100227#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +0200228#define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT
Johannes Schindelin3bef5d82017-08-08 16:46:39 +0200229#define BASH_READ_D ENABLE_ASH_BASH_COMPAT
230#define IF_BASH_READ_D IF_ASH_BASH_COMPAT
Ron Yorstone48559e2019-03-31 09:27:09 +0100231#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100232
Denys Vlasenko67047462016-12-22 15:21:58 +0100233#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
234/* Bionic at least up to version 24 has no glob() */
235# undef ENABLE_ASH_INTERNAL_GLOB
236# define ENABLE_ASH_INTERNAL_GLOB 1
237#endif
238
239#if !ENABLE_ASH_INTERNAL_GLOB && defined(__UCLIBC__)
240# error uClibc glob() is buggy, use ASH_INTERNAL_GLOB.
241# error The bug is: for "$PWD"/<pattern> ash will escape e.g. dashes in "$PWD"
242# error with backslash, even ones which do not need to be: "/a-b" -> "/a\-b"
243# error glob() should unbackslash them and match. uClibc does not unbackslash,
244# error fails to match dirname, subsequently not expanding <pattern> in it.
245// Testcase:
246// if (glob("/etc/polkit\\-1", 0, NULL, &pglob)) - this returns 0 on uclibc, no bug
247// if (glob("/etc/polkit\\-1/*", 0, NULL, &pglob)) printf("uclibc bug!\n");
248#endif
249
250#if !ENABLE_ASH_INTERNAL_GLOB
251# include <glob.h>
252#endif
253
254#include "unicode.h"
255#include "shell_common.h"
Denys Vlasenko0b883582016-12-23 16:49:07 +0100256#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko67047462016-12-22 15:21:58 +0100257# include "math.h"
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200258#else
259typedef long arith_t;
260# define ARITH_FMT "%ld"
Denys Vlasenko67047462016-12-22 15:21:58 +0100261#endif
262#if ENABLE_ASH_RANDOM_SUPPORT
263# include "random.h"
264#else
265# define CLEAR_RANDOM_T(rnd) ((void)0)
266#endif
267
268#include "NUM_APPLETS.h"
269#if NUM_APPLETS == 1
270/* STANDALONE does not make sense, and won't compile */
271# undef CONFIG_FEATURE_SH_STANDALONE
272# undef ENABLE_FEATURE_SH_STANDALONE
273# undef IF_FEATURE_SH_STANDALONE
274# undef IF_NOT_FEATURE_SH_STANDALONE
275# define ENABLE_FEATURE_SH_STANDALONE 0
276# define IF_FEATURE_SH_STANDALONE(...)
277# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
278#endif
279
Denys Vlasenko9acd63c2018-03-28 18:35:07 +0200280#ifndef F_DUPFD_CLOEXEC
281# define F_DUPFD_CLOEXEC F_DUPFD
282#endif
Denys Vlasenko60fb98e2018-03-30 22:15:14 +0200283#ifndef O_CLOEXEC
284# define O_CLOEXEC 0
285#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100286#ifndef PIPE_BUF
287# define PIPE_BUF 4096 /* amount of buffering in a pipe */
288#endif
289
290#if !BB_MMU
291# error "Do not even bother, ash will not run on NOMMU machine"
292#endif
293
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100294/* We use a trick to have more optimized code (fewer pointer reloads):
295 * ash.c: extern struct globals *const ash_ptr_to_globals;
296 * ash_ptr_hack.c: struct globals *ash_ptr_to_globals;
297 * This way, compiler in ash.c knows the pointer can not change.
298 *
299 * However, this may break on weird arches or toolchains. In this case,
300 * set "-DBB_GLOBAL_CONST=''" in CONFIG_EXTRA_CFLAGS to disable
301 * this optimization.
302 */
303#ifndef BB_GLOBAL_CONST
304# define BB_GLOBAL_CONST const
305#endif
306
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307
Denis Vlasenko01631112007-12-16 17:20:38 +0000308/* ============ Hash table sizes. Configurable. */
309
310#define VTABSIZE 39
311#define ATABSIZE 39
312#define CMDTABLESIZE 31 /* should be prime */
313
314
Denis Vlasenkob012b102007-02-19 22:43:01 +0000315/* ============ Shell options */
316
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100317/* If you add/change options hare, update --help text too */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000318static const char *const optletters_optnames[] = {
319 "e" "errexit",
320 "f" "noglob",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100321/* bash has '-o ignoreeof', but no short synonym -I for it */
322/* (in bash, set -I disables invisible variables (what's that?)) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323 "I" "ignoreeof",
Denys Vlasenkof3634582019-06-03 12:21:04 +0200324/* The below allowed this invocation:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200325 * ash -c 'set -i; echo $-; sleep 5; echo $-'
326 * to be ^C-ed and get to interactive ash prompt.
Denys Vlasenkof3634582019-06-03 12:21:04 +0200327 * bash does not support such "set -i".
328 * In our code, this is denoted by empty long name:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200329 */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200330 "i" "",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100331/* (removing "i" altogether would remove it from "$-", not good) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000332 "m" "monitor",
333 "n" "noexec",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100334/* Ditto: bash has no "set -s", "set -c" */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200335 "s" "",
336 "c" "",
Denis Vlasenkob012b102007-02-19 22:43:01 +0000337 "x" "xtrace",
338 "v" "verbose",
339 "C" "noclobber",
340 "a" "allexport",
341 "b" "notify",
342 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100343 "\0" "vi"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100344#if BASH_PIPEFAIL
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100345 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100346#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000347#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000348 ,"\0" "nolog"
349 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000350#endif
351};
Denys Vlasenko897475a2019-06-01 16:35:09 +0200352//bash 4.4.23 also has these opts (with these defaults):
353//braceexpand on
354//emacs on
355//errtrace off
356//functrace off
357//hashall on
358//histexpand off
359//history on
360//interactive-comments on
361//keyword off
362//onecmd off
363//physical off
364//posix off
365//privileged off
Denis Vlasenkob012b102007-02-19 22:43:01 +0000366
Denys Vlasenko285ad152009-12-04 23:02:27 +0100367#define optletters(n) optletters_optnames[n][0]
368#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000369
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000370enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000371
Eric Andersenc470f442003-07-28 09:56:35 +0000372
Denis Vlasenkob012b102007-02-19 22:43:01 +0000373/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000374
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200375#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000376
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000377/*
Eric Andersenc470f442003-07-28 09:56:35 +0000378 * We enclose jmp_buf in a structure so that we can declare pointers to
379 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000380 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000381 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000382 * exception handlers, the user should save the value of handler on entry
383 * to an inner scope, set handler to point to a jmploc structure for the
384 * inner scope, and restore handler on exit from the scope.
385 */
Eric Andersenc470f442003-07-28 09:56:35 +0000386struct jmploc {
387 jmp_buf loc;
388};
Denis Vlasenko01631112007-12-16 17:20:38 +0000389
390struct globals_misc {
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200391 uint8_t exitstatus; /* exit status of last command */
392 uint8_t back_exitstatus;/* exit status of backquoted command */
393 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100394 int savestatus; /* exit status of last command outside traps */
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200395 int rootpid; /* pid of main shell */
Denis Vlasenko01631112007-12-16 17:20:38 +0000396 /* shell level: 0 for the main shell, 1 for its children, and so on */
397 int shlvl;
398#define rootshell (!shlvl)
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100399 int errlinno;
400
Denis Vlasenko01631112007-12-16 17:20:38 +0000401 char *minusc; /* argument to -c option */
402
403 char *curdir; // = nullstr; /* current working directory */
404 char *physdir; // = nullstr; /* physical working directory */
405
406 char *arg0; /* value of $0 */
407
408 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000409
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200410 volatile int suppress_int; /* counter */
411 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200412 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
Denys Vlasenko8f7b0242016-10-28 17:16:11 +0200413 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100414 smallint exception_type; /* kind of exception: */
Eric Andersenc470f442003-07-28 09:56:35 +0000415#define EXINT 0 /* SIGINT received */
416#define EXERROR 1 /* a generic error */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100417#define EXEND 3 /* exit the shell */
418#define EXEXIT 4 /* exit the shell via exitcmd */
Eric Andersen2870d962001-07-02 17:27:21 +0000419
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000420 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000421
422 char optlist[NOPTS];
423#define eflag optlist[0]
424#define fflag optlist[1]
425#define Iflag optlist[2]
426#define iflag optlist[3]
427#define mflag optlist[4]
428#define nflag optlist[5]
429#define sflag optlist[6]
Denys Vlasenkof3634582019-06-03 12:21:04 +0200430#define cflag optlist[7]
431#define xflag optlist[8]
432#define vflag optlist[9]
433#define Cflag optlist[10]
434#define aflag optlist[11]
435#define bflag optlist[12]
436#define uflag optlist[13]
437#define viflag optlist[14]
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100438#if BASH_PIPEFAIL
Denys Vlasenkof3634582019-06-03 12:21:04 +0200439# define pipefail optlist[15]
Michael Abbott359da5e2009-12-04 23:03:29 +0100440#else
441# define pipefail 0
442#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000443#if DEBUG
Denys Vlasenkof3634582019-06-03 12:21:04 +0200444# define nolog optlist[15 + BASH_PIPEFAIL]
445# define debug optlist[16 + BASH_PIPEFAIL]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000446#endif
447
448 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000449 /*
450 * Sigmode records the current value of the signal handlers for the various
451 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000452 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000453 */
454 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000455#define S_DFL 1 /* default signal handling (SIG_DFL) */
456#define S_CATCH 2 /* signal is caught */
457#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denys Vlasenko0f14f412017-08-06 20:06:19 +0200458#define S_HARD_IGN 4 /* signal is ignored permanently (it was SIG_IGN on entry to shell) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000459
Denis Vlasenko01631112007-12-16 17:20:38 +0000460 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000461 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200462 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000463 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200464 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000465
466 /* Rarely referenced stuff */
467#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200468 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000469#endif
470 pid_t backgndpid; /* pid of last background process */
Denis Vlasenko01631112007-12-16 17:20:38 +0000471};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100472extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000473#define G_misc (*ash_ptr_to_globals_misc)
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200474#define exitstatus (G_misc.exitstatus )
475#define back_exitstatus (G_misc.back_exitstatus )
476#define job_warning (G_misc.job_warning)
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100477#define savestatus (G_misc.savestatus )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000478#define rootpid (G_misc.rootpid )
479#define shlvl (G_misc.shlvl )
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100480#define errlinno (G_misc.errlinno )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000481#define minusc (G_misc.minusc )
482#define curdir (G_misc.curdir )
483#define physdir (G_misc.physdir )
484#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000485#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000486#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200487#define suppress_int (G_misc.suppress_int )
488#define pending_int (G_misc.pending_int )
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200489#define got_sigchld (G_misc.got_sigchld )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200490#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000491#define nullstr (G_misc.nullstr )
492#define optlist (G_misc.optlist )
493#define sigmode (G_misc.sigmode )
494#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200495#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000496#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200497#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200498#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000499#define backgndpid (G_misc.backgndpid )
Denis Vlasenko01631112007-12-16 17:20:38 +0000500#define INIT_G_misc() do { \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +0200501 (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000502 barrier(); \
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100503 savestatus = -1; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000504 curdir = nullstr; \
505 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200506 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000507} while (0)
508
509
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000510/* ============ DEBUG */
511#if DEBUG
512static void trace_printf(const char *fmt, ...);
513static void trace_vprintf(const char *fmt, va_list va);
514# define TRACE(param) trace_printf param
515# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000516# define close(fd) do { \
517 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000518 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200519 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000520 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000521} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000522#else
523# define TRACE(param)
524# define TRACEV(param)
525#endif
526
527
Denis Vlasenko559691a2008-10-05 18:39:31 +0000528/* ============ Utility functions */
Denys Vlasenko1961aea2013-02-26 00:36:53 +0100529#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
530#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
531
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200532static int
533isdigit_str9(const char *str)
Denis Vlasenko559691a2008-10-05 18:39:31 +0000534{
535 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
536 while (--maxlen && isdigit(*str))
537 str++;
538 return (*str == '\0');
539}
Denis Vlasenko01631112007-12-16 17:20:38 +0000540
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200541static const char *
542var_end(const char *var)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200543{
544 while (*var)
545 if (*var++ == '=')
546 break;
547 return var;
548}
549
Denis Vlasenko559691a2008-10-05 18:39:31 +0000550
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100551/* ============ Parser data */
552
553/*
554 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
555 */
556struct strlist {
557 struct strlist *next;
558 char *text;
559};
560
561struct alias;
562
563struct strpush {
564 struct strpush *prev; /* preceding string on stack */
565 char *prev_string;
566 int prev_left_in_line;
567#if ENABLE_ASH_ALIAS
568 struct alias *ap; /* if push was associated with an alias */
569#endif
570 char *string; /* remember the string since it may change */
571
572 /* Remember last two characters for pungetc. */
573 int lastc[2];
574
575 /* Number of outstanding calls to pungetc. */
576 int unget;
577};
578
579/*
580 * The parsefile structure pointed to by the global variable parsefile
581 * contains information about the current file being read.
582 */
583struct parsefile {
584 struct parsefile *prev; /* preceding file on stack */
585 int linno; /* current line */
586 int pf_fd; /* file descriptor (or -1 if string) */
587 int left_in_line; /* number of chars left in this line */
588 int left_in_buffer; /* number of chars left in this buffer past the line */
589 char *next_to_pgetc; /* next char in buffer */
590 char *buf; /* input buffer */
591 struct strpush *strpush; /* for pushing strings at this level */
592 struct strpush basestrpush; /* so pushing one is fast */
593
594 /* Remember last two characters for pungetc. */
595 int lastc[2];
596
597 /* Number of outstanding calls to pungetc. */
598 int unget;
599};
600
601static struct parsefile basepf; /* top level input file */
602static struct parsefile *g_parsefile = &basepf; /* current input file */
603static char *commandname; /* currently executing command */
604
605
Denis Vlasenko559691a2008-10-05 18:39:31 +0000606/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100607
608static void exitshell(void) NORETURN;
609
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000610/*
Eric Andersen2870d962001-07-02 17:27:21 +0000611 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000612 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000613 * much more efficient and portable. (But hacking the kernel is so much
614 * more fun than worrying about efficiency and portability. :-))
615 */
Denys Vlasenko06b11492016-11-04 16:43:18 +0100616#if DEBUG_INTONOFF
617# define INT_OFF do { \
618 TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200619 suppress_int++; \
Denys Vlasenkode892052016-10-02 01:49:13 +0200620 barrier(); \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000621} while (0)
Denys Vlasenko06b11492016-11-04 16:43:18 +0100622#else
623# define INT_OFF do { \
624 suppress_int++; \
625 barrier(); \
626} while (0)
627#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000628
629/*
630 * Called to raise an exception. Since C doesn't include exceptions, we
631 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000632 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000633 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000634static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000635static void
636raise_exception(int e)
637{
638#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000639 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000640 abort();
641#endif
642 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000643 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000644 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000645}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000646#if DEBUG
647#define raise_exception(e) do { \
648 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
649 raise_exception(e); \
650} while (0)
651#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000652
653/*
Denys Vlasenkof37e1152016-10-07 03:17:28 +0200654 * Called when a SIGINT is received. (If the user specifies
Denis Vlasenkob012b102007-02-19 22:43:01 +0000655 * that SIGINT is to be trapped or ignored using the trap builtin, then
656 * this routine is not called.) Suppressint is nonzero when interrupts
657 * are held using the INT_OFF macro. (The test for iflag is just
658 * defensive programming.)
659 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000660static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000661static void
662raise_interrupt(void)
663{
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200664 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000665 /* Signal is not automatically unmasked after it is raised,
666 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000667 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200668 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000669
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200670 if (!(rootshell && iflag)) {
671 /* Kill ourself with SIGINT */
672 signal(SIGINT, SIG_DFL);
673 raise(SIGINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000674 }
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200675 /* bash: ^C even on empty command line sets $? */
676 exitstatus = SIGINT + 128;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200677 raise_exception(EXINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000678 /* NOTREACHED */
679}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000680#if DEBUG
681#define raise_interrupt() do { \
682 TRACE(("raising interrupt on line %d\n", __LINE__)); \
683 raise_interrupt(); \
684} while (0)
685#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000686
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000687static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000688int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000689{
Denys Vlasenkode892052016-10-02 01:49:13 +0200690 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200691 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000692 raise_interrupt();
693 }
694}
Denys Vlasenko06b11492016-11-04 16:43:18 +0100695#if DEBUG_INTONOFF
696# define INT_ON do { \
697 TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \
698 int_on(); \
699} while (0)
700#else
701# define INT_ON int_on()
702#endif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000703static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000704force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000705{
Denys Vlasenkode892052016-10-02 01:49:13 +0200706 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200707 suppress_int = 0;
708 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000709 raise_interrupt();
710}
711#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000712
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200713#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000714
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000715#define RESTORE_INT(v) do { \
Denys Vlasenkode892052016-10-02 01:49:13 +0200716 barrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200717 suppress_int = (v); \
718 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000719 raise_interrupt(); \
720} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000721
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000722
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000723/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000724
Eric Andersenc470f442003-07-28 09:56:35 +0000725static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000726outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000727{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000728 INT_OFF;
729 fputs(p, file);
730 INT_ON;
731}
732
733static void
734flush_stdout_stderr(void)
735{
736 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100737 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000738 INT_ON;
739}
740
Denys Vlasenko9c541002015-10-07 15:44:36 +0200741/* Was called outcslow(c,FILE*), but c was always '\n' */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000742static void
Denys Vlasenko9c541002015-10-07 15:44:36 +0200743newline_and_flush(FILE *dest)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000744{
745 INT_OFF;
Denys Vlasenko9c541002015-10-07 15:44:36 +0200746 putc('\n', dest);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000747 fflush(dest);
748 INT_ON;
749}
750
751static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
752static int
753out1fmt(const char *fmt, ...)
754{
755 va_list ap;
756 int r;
757
758 INT_OFF;
759 va_start(ap, fmt);
760 r = vprintf(fmt, ap);
761 va_end(ap);
762 INT_ON;
763 return r;
764}
765
766static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
767static int
768fmtstr(char *outbuf, size_t length, const char *fmt, ...)
769{
770 va_list ap;
771 int ret;
772
Denis Vlasenkob012b102007-02-19 22:43:01 +0000773 INT_OFF;
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200774 va_start(ap, fmt);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000775 ret = vsnprintf(outbuf, length, fmt, ap);
776 va_end(ap);
777 INT_ON;
Denys Vlasenko3f7fb2c2020-02-16 18:06:20 +0100778 return ret > (int)length ? length : ret;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000779}
780
781static void
782out1str(const char *p)
783{
784 outstr(p, stdout);
785}
786
787static void
788out2str(const char *p)
789{
790 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100791 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000792}
793
794
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000795/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000796
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000797/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100798#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200799#define CTLESC ((unsigned char)'\201') /* escape next character */
800#define CTLVAR ((unsigned char)'\202') /* variable defn */
801#define CTLENDVAR ((unsigned char)'\203')
802#define CTLBACKQ ((unsigned char)'\204')
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200803#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
804#define CTLENDARI ((unsigned char)'\207')
805#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100806#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000807
808/* variable substitution byte (follows CTLVAR) */
809#define VSTYPE 0x0f /* type of variable substitution */
810#define VSNUL 0x10 /* colon--treat the empty string as unset */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000811
812/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000813#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
814#define VSMINUS 0x2 /* ${var-text} */
815#define VSPLUS 0x3 /* ${var+text} */
816#define VSQUESTION 0x4 /* ${var?message} */
817#define VSASSIGN 0x5 /* ${var=text} */
818#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
819#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
820#define VSTRIMLEFT 0x8 /* ${var#pattern} */
821#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
822#define VSLENGTH 0xa /* ${#var} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100823#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000824#define VSSUBSTR 0xc /* ${var:position:length} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100825#endif
826#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000827#define VSREPLACE 0xd /* ${var/pattern/replacement} */
828#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
829#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000830
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000831static const char dolatstr[] ALIGN1 = {
Ron Yorston549deab2015-05-18 09:57:51 +0200832 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000833};
Ron Yorston549deab2015-05-18 09:57:51 +0200834#define DOLATSTRLEN 6
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000835
Denis Vlasenko559691a2008-10-05 18:39:31 +0000836#define NCMD 0
837#define NPIPE 1
838#define NREDIR 2
839#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000840#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000841#define NAND 5
842#define NOR 6
843#define NSEMI 7
844#define NIF 8
845#define NWHILE 9
846#define NUNTIL 10
847#define NFOR 11
848#define NCASE 12
849#define NCLIST 13
850#define NDEFUN 14
851#define NARG 15
852#define NTO 16
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100853#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +0000854#define NTO2 17
855#endif
856#define NCLOBBER 18
857#define NFROM 19
858#define NFROMTO 20
859#define NAPPEND 21
860#define NTOFD 22
861#define NFROMFD 23
862#define NHERE 24
863#define NXHERE 25
864#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000865#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000866
867union node;
868
869struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000870 smallint type; /* Nxxxx */
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100871 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000872 union node *assign;
873 union node *args;
874 union node *redirect;
875};
876
877struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000878 smallint type;
879 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000880 struct nodelist *cmdlist;
881};
882
883struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000884 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100885 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000886 union node *n;
887 union node *redirect;
888};
889
890struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000891 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000892 union node *ch1;
893 union node *ch2;
894};
895
896struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000897 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000898 union node *test;
899 union node *ifpart;
900 union node *elsepart;
901};
902
903struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000904 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100905 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000906 union node *args;
907 union node *body;
908 char *var;
909};
910
911struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000912 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100913 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000914 union node *expr;
915 union node *cases;
916};
917
918struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000919 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000920 union node *next;
921 union node *pattern;
922 union node *body;
923};
924
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100925struct ndefun {
926 smallint type;
927 int linno;
928 char *text;
929 union node *body;
930};
931
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000932struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000933 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000934 union node *next;
935 char *text;
936 struct nodelist *backquote;
937};
938
Denis Vlasenko559691a2008-10-05 18:39:31 +0000939/* nfile and ndup layout must match!
940 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
941 * that it is actually NTO2 (>&file), and change its type.
942 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000943struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000944 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000945 union node *next;
946 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000947 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000948 union node *fname;
949 char *expfname;
950};
951
952struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000953 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000954 union node *next;
955 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000956 int dupfd;
957 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000958 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000959};
960
961struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000962 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000963 union node *next;
964 int fd;
965 union node *doc;
966};
967
968struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000969 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000970 union node *com;
971};
972
973union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000974 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000975 struct ncmd ncmd;
976 struct npipe npipe;
977 struct nredir nredir;
978 struct nbinary nbinary;
979 struct nif nif;
980 struct nfor nfor;
981 struct ncase ncase;
982 struct nclist nclist;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100983 struct ndefun ndefun;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000984 struct narg narg;
985 struct nfile nfile;
986 struct ndup ndup;
987 struct nhere nhere;
988 struct nnot nnot;
989};
990
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200991/*
992 * NODE_EOF is returned by parsecmd when it encounters an end of file.
993 * It must be distinct from NULL.
994 */
995#define NODE_EOF ((union node *) -1L)
996
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000997struct nodelist {
998 struct nodelist *next;
999 union node *n;
1000};
1001
1002struct funcnode {
1003 int count;
1004 union node n;
1005};
1006
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001007/*
1008 * Free a parse tree.
1009 */
1010static void
1011freefunc(struct funcnode *f)
1012{
1013 if (f && --f->count < 0)
1014 free(f);
1015}
1016
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001017
1018/* ============ Debugging output */
1019
1020#if DEBUG
1021
1022static FILE *tracefile;
1023
1024static void
1025trace_printf(const char *fmt, ...)
1026{
1027 va_list va;
1028
1029 if (debug != 1)
1030 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00001031 if (DEBUG_TIME)
1032 fprintf(tracefile, "%u ", (int) time(NULL));
1033 if (DEBUG_PID)
1034 fprintf(tracefile, "[%u] ", (int) getpid());
1035 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001036 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001037 va_start(va, fmt);
1038 vfprintf(tracefile, fmt, va);
1039 va_end(va);
1040}
1041
1042static void
1043trace_vprintf(const char *fmt, va_list va)
1044{
1045 if (debug != 1)
1046 return;
1047 vfprintf(tracefile, fmt, va);
Denys Vlasenko474ed062016-10-30 18:30:29 +01001048 fprintf(tracefile, "\n");
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001049}
1050
1051static void
1052trace_puts(const char *s)
1053{
1054 if (debug != 1)
1055 return;
1056 fputs(s, tracefile);
1057}
1058
1059static void
1060trace_puts_quoted(char *s)
1061{
1062 char *p;
1063 char c;
1064
1065 if (debug != 1)
1066 return;
1067 putc('"', tracefile);
1068 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001069 switch ((unsigned char)*p) {
1070 case '\n': c = 'n'; goto backslash;
1071 case '\t': c = 't'; goto backslash;
1072 case '\r': c = 'r'; goto backslash;
1073 case '\"': c = '\"'; goto backslash;
1074 case '\\': c = '\\'; goto backslash;
1075 case CTLESC: c = 'e'; goto backslash;
1076 case CTLVAR: c = 'v'; goto backslash;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001077 case CTLBACKQ: c = 'q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001078 backslash:
1079 putc('\\', tracefile);
1080 putc(c, tracefile);
1081 break;
1082 default:
1083 if (*p >= ' ' && *p <= '~')
1084 putc(*p, tracefile);
1085 else {
1086 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +01001087 putc((*p >> 6) & 03, tracefile);
1088 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001089 putc(*p & 07, tracefile);
1090 }
1091 break;
1092 }
1093 }
1094 putc('"', tracefile);
1095}
1096
1097static void
1098trace_puts_args(char **ap)
1099{
1100 if (debug != 1)
1101 return;
1102 if (!*ap)
1103 return;
1104 while (1) {
1105 trace_puts_quoted(*ap);
1106 if (!*++ap) {
1107 putc('\n', tracefile);
1108 break;
1109 }
1110 putc(' ', tracefile);
1111 }
1112}
1113
1114static void
1115opentrace(void)
1116{
1117 char s[100];
1118#ifdef O_APPEND
1119 int flags;
1120#endif
1121
1122 if (debug != 1) {
1123 if (tracefile)
1124 fflush(tracefile);
1125 /* leave open because libedit might be using it */
1126 return;
1127 }
1128 strcpy(s, "./trace");
1129 if (tracefile) {
1130 if (!freopen(s, "a", tracefile)) {
1131 fprintf(stderr, "Can't re-open %s\n", s);
1132 debug = 0;
1133 return;
1134 }
1135 } else {
1136 tracefile = fopen(s, "a");
1137 if (tracefile == NULL) {
1138 fprintf(stderr, "Can't open %s\n", s);
1139 debug = 0;
1140 return;
1141 }
1142 }
1143#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001144 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001145 if (flags >= 0)
1146 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
1147#endif
1148 setlinebuf(tracefile);
1149 fputs("\nTracing started.\n", tracefile);
1150}
1151
1152static void
1153indent(int amount, char *pfx, FILE *fp)
1154{
1155 int i;
1156
1157 for (i = 0; i < amount; i++) {
1158 if (pfx && i == amount - 1)
1159 fputs(pfx, fp);
1160 putc('\t', fp);
1161 }
1162}
1163
1164/* little circular references here... */
1165static void shtree(union node *n, int ind, char *pfx, FILE *fp);
1166
1167static void
1168sharg(union node *arg, FILE *fp)
1169{
1170 char *p;
1171 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001172 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001173
1174 if (arg->type != NARG) {
1175 out1fmt("<node type %d>\n", arg->type);
1176 abort();
1177 }
1178 bqlist = arg->narg.backquote;
1179 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001180 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001181 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -07001182 p++;
1183 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001184 break;
1185 case CTLVAR:
1186 putc('$', fp);
1187 putc('{', fp);
1188 subtype = *++p;
1189 if (subtype == VSLENGTH)
1190 putc('#', fp);
1191
Dan Fandrich77d48722010-09-07 23:38:28 -07001192 while (*p != '=') {
1193 putc(*p, fp);
1194 p++;
1195 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001196
1197 if (subtype & VSNUL)
1198 putc(':', fp);
1199
1200 switch (subtype & VSTYPE) {
1201 case VSNORMAL:
1202 putc('}', fp);
1203 break;
1204 case VSMINUS:
1205 putc('-', fp);
1206 break;
1207 case VSPLUS:
1208 putc('+', fp);
1209 break;
1210 case VSQUESTION:
1211 putc('?', fp);
1212 break;
1213 case VSASSIGN:
1214 putc('=', fp);
1215 break;
1216 case VSTRIMLEFT:
1217 putc('#', fp);
1218 break;
1219 case VSTRIMLEFTMAX:
1220 putc('#', fp);
1221 putc('#', fp);
1222 break;
1223 case VSTRIMRIGHT:
1224 putc('%', fp);
1225 break;
1226 case VSTRIMRIGHTMAX:
1227 putc('%', fp);
1228 putc('%', fp);
1229 break;
1230 case VSLENGTH:
1231 break;
1232 default:
1233 out1fmt("<subtype %d>", subtype);
1234 }
1235 break;
1236 case CTLENDVAR:
1237 putc('}', fp);
1238 break;
1239 case CTLBACKQ:
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001240 putc('$', fp);
1241 putc('(', fp);
1242 shtree(bqlist->n, -1, NULL, fp);
1243 putc(')', fp);
1244 break;
1245 default:
1246 putc(*p, fp);
1247 break;
1248 }
1249 }
1250}
1251
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001252static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001253shcmd(union node *cmd, FILE *fp)
1254{
1255 union node *np;
1256 int first;
1257 const char *s;
1258 int dftfd;
1259
1260 first = 1;
1261 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001262 if (!first)
1263 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001264 sharg(np, fp);
1265 first = 0;
1266 }
1267 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001268 if (!first)
1269 putc(' ', fp);
1270 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001271 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001272 case NTO: s = ">>"+1; dftfd = 1; break;
1273 case NCLOBBER: s = ">|"; dftfd = 1; break;
1274 case NAPPEND: s = ">>"; dftfd = 1; break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01001275#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00001276 case NTO2:
1277#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001278 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001279 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001280 case NFROMFD: s = "<&"; break;
1281 case NFROMTO: s = "<>"; break;
1282 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001283 }
1284 if (np->nfile.fd != dftfd)
1285 fprintf(fp, "%d", np->nfile.fd);
1286 fputs(s, fp);
1287 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1288 fprintf(fp, "%d", np->ndup.dupfd);
1289 } else {
1290 sharg(np->nfile.fname, fp);
1291 }
1292 first = 0;
1293 }
1294}
1295
1296static void
1297shtree(union node *n, int ind, char *pfx, FILE *fp)
1298{
1299 struct nodelist *lp;
1300 const char *s;
1301
1302 if (n == NULL)
1303 return;
1304
1305 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001306
1307 if (n == NODE_EOF) {
1308 fputs("<EOF>", fp);
1309 return;
1310 }
1311
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001312 switch (n->type) {
1313 case NSEMI:
1314 s = "; ";
1315 goto binop;
1316 case NAND:
1317 s = " && ";
1318 goto binop;
1319 case NOR:
1320 s = " || ";
1321 binop:
1322 shtree(n->nbinary.ch1, ind, NULL, fp);
1323 /* if (ind < 0) */
1324 fputs(s, fp);
1325 shtree(n->nbinary.ch2, ind, NULL, fp);
1326 break;
1327 case NCMD:
1328 shcmd(n, fp);
1329 if (ind >= 0)
1330 putc('\n', fp);
1331 break;
1332 case NPIPE:
1333 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001334 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001335 if (lp->next)
1336 fputs(" | ", fp);
1337 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001338 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001339 fputs(" &", fp);
1340 if (ind >= 0)
1341 putc('\n', fp);
1342 break;
1343 default:
1344 fprintf(fp, "<node type %d>", n->type);
1345 if (ind >= 0)
1346 putc('\n', fp);
1347 break;
1348 }
1349}
1350
1351static void
1352showtree(union node *n)
1353{
1354 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001355 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001356}
1357
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001358#endif /* DEBUG */
1359
1360
Denis Vlasenkob012b102007-02-19 22:43:01 +00001361/* ============ Message printing */
1362
1363static void
1364ash_vmsg(const char *msg, va_list ap)
1365{
1366 fprintf(stderr, "%s: ", arg0);
1367 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001368 if (strcmp(arg0, commandname))
1369 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001370 if (!iflag || g_parsefile->pf_fd > 0)
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001371 fprintf(stderr, "line %d: ", errlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001372 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001373 vfprintf(stderr, msg, ap);
Denys Vlasenko9c541002015-10-07 15:44:36 +02001374 newline_and_flush(stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001375}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001376
1377/*
1378 * Exverror is called to raise the error exception. If the second argument
1379 * is not NULL then error prints an error message using printf style
1380 * formatting. It then raises the error exception.
1381 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001382static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001383static void
1384ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001385{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001386#if DEBUG
1387 if (msg) {
Denys Vlasenko474ed062016-10-30 18:30:29 +01001388 TRACE(("ash_vmsg_and_raise(%d):", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001389 TRACEV((msg, ap));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001390 } else
Denys Vlasenko474ed062016-10-30 18:30:29 +01001391 TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001392 if (msg)
1393#endif
1394 ash_vmsg(msg, ap);
1395
1396 flush_stdout_stderr();
1397 raise_exception(cond);
1398 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001399}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001400
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001401static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001402static void
1403ash_msg_and_raise_error(const char *msg, ...)
1404{
1405 va_list ap;
1406
Ron Yorstonea7d2f62017-01-03 11:18:23 +01001407 exitstatus = 2;
1408
Denis Vlasenkob012b102007-02-19 22:43:01 +00001409 va_start(ap, msg);
1410 ash_vmsg_and_raise(EXERROR, msg, ap);
1411 /* NOTREACHED */
1412 va_end(ap);
1413}
1414
Ron Yorstonbe366e52017-07-27 13:53:39 +01001415/*
Ron Yorstonbe366e52017-07-27 13:53:39 +01001416 * 'fmt' must be a string literal.
1417 */
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001418#define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": "STRERROR_FMT, ##__VA_ARGS__ STRERROR_ERRNO)
Ron Yorstonbe366e52017-07-27 13:53:39 +01001419
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001420static void raise_error_syntax(const char *) NORETURN;
1421static void
1422raise_error_syntax(const char *msg)
1423{
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001424 errlinno = g_parsefile->linno;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001425 ash_msg_and_raise_error("syntax error: %s", msg);
1426 /* NOTREACHED */
1427}
1428
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001429static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001430static void
1431ash_msg_and_raise(int cond, const char *msg, ...)
1432{
1433 va_list ap;
1434
1435 va_start(ap, msg);
1436 ash_vmsg_and_raise(cond, msg, ap);
1437 /* NOTREACHED */
1438 va_end(ap);
1439}
1440
1441/*
1442 * error/warning routines for external builtins
1443 */
1444static void
1445ash_msg(const char *fmt, ...)
1446{
1447 va_list ap;
1448
1449 va_start(ap, fmt);
1450 ash_vmsg(fmt, ap);
1451 va_end(ap);
1452}
1453
1454/*
1455 * Return a string describing an error. The returned string may be a
1456 * pointer to a static buffer that will be overwritten on the next call.
1457 * Action describes the operation that got the error.
1458 */
1459static const char *
1460errmsg(int e, const char *em)
1461{
1462 if (e == ENOENT || e == ENOTDIR) {
1463 return em;
1464 }
1465 return strerror(e);
1466}
1467
1468
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001469/* ============ Memory allocation */
1470
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001471#if 0
1472/* I consider these wrappers nearly useless:
1473 * ok, they return you to nearest exception handler, but
1474 * how much memory do you leak in the process, making
1475 * memory starvation worse?
1476 */
1477static void *
1478ckrealloc(void * p, size_t nbytes)
1479{
1480 p = realloc(p, nbytes);
1481 if (!p)
1482 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1483 return p;
1484}
1485
1486static void *
1487ckmalloc(size_t nbytes)
1488{
1489 return ckrealloc(NULL, nbytes);
1490}
1491
1492static void *
1493ckzalloc(size_t nbytes)
1494{
1495 return memset(ckmalloc(nbytes), 0, nbytes);
1496}
1497
1498static char *
1499ckstrdup(const char *s)
1500{
1501 char *p = strdup(s);
1502 if (!p)
1503 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1504 return p;
1505}
1506#else
1507/* Using bbox equivalents. They exit if out of memory */
1508# define ckrealloc xrealloc
1509# define ckmalloc xmalloc
1510# define ckzalloc xzalloc
1511# define ckstrdup xstrdup
1512#endif
1513
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001514/*
1515 * It appears that grabstackstr() will barf with such alignments
1516 * because stalloc() will return a string allocated in a new stackblock.
1517 */
1518#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1519enum {
1520 /* Most machines require the value returned from malloc to be aligned
1521 * in some way. The following macro will get this right
1522 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001523 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001524 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001525 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001526};
1527
1528struct stack_block {
1529 struct stack_block *prev;
1530 char space[MINSIZE];
1531};
1532
1533struct stackmark {
1534 struct stack_block *stackp;
1535 char *stacknxt;
1536 size_t stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001537};
1538
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001539
Denis Vlasenko01631112007-12-16 17:20:38 +00001540struct globals_memstack {
1541 struct stack_block *g_stackp; // = &stackbase;
Denis Vlasenko01631112007-12-16 17:20:38 +00001542 char *g_stacknxt; // = stackbase.space;
1543 char *sstrend; // = stackbase.space + MINSIZE;
1544 size_t g_stacknleft; // = MINSIZE;
Denis Vlasenko01631112007-12-16 17:20:38 +00001545 struct stack_block stackbase;
1546};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01001547extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001548#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001549#define g_stackp (G_memstack.g_stackp )
Denis Vlasenko01631112007-12-16 17:20:38 +00001550#define g_stacknxt (G_memstack.g_stacknxt )
1551#define sstrend (G_memstack.sstrend )
1552#define g_stacknleft (G_memstack.g_stacknleft)
Denis Vlasenko01631112007-12-16 17:20:38 +00001553#define stackbase (G_memstack.stackbase )
1554#define INIT_G_memstack() do { \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +02001555 (*(struct globals_memstack**)not_const_pp(&ash_ptr_to_globals_memstack)) = xzalloc(sizeof(G_memstack)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001556 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001557 g_stackp = &stackbase; \
1558 g_stacknxt = stackbase.space; \
1559 g_stacknleft = MINSIZE; \
1560 sstrend = stackbase.space + MINSIZE; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001561} while (0)
1562
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001563
Denis Vlasenko01631112007-12-16 17:20:38 +00001564#define stackblock() ((void *)g_stacknxt)
1565#define stackblocksize() g_stacknleft
1566
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001567/*
1568 * Parse trees for commands are allocated in lifo order, so we use a stack
1569 * to make this more efficient, and also to avoid all sorts of exception
1570 * handling code to handle interrupts in the middle of a parse.
1571 *
1572 * The size 504 was chosen because the Ultrix malloc handles that size
1573 * well.
1574 */
1575static void *
1576stalloc(size_t nbytes)
1577{
1578 char *p;
1579 size_t aligned;
1580
1581 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001582 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001583 size_t len;
1584 size_t blocksize;
1585 struct stack_block *sp;
1586
1587 blocksize = aligned;
1588 if (blocksize < MINSIZE)
1589 blocksize = MINSIZE;
1590 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1591 if (len < blocksize)
1592 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1593 INT_OFF;
1594 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001595 sp->prev = g_stackp;
1596 g_stacknxt = sp->space;
1597 g_stacknleft = blocksize;
1598 sstrend = g_stacknxt + blocksize;
1599 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001600 INT_ON;
1601 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001602 p = g_stacknxt;
1603 g_stacknxt += aligned;
1604 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001605 return p;
1606}
1607
Denis Vlasenko597906c2008-02-20 16:38:54 +00001608static void *
1609stzalloc(size_t nbytes)
1610{
1611 return memset(stalloc(nbytes), 0, nbytes);
1612}
1613
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001614static void
1615stunalloc(void *p)
1616{
1617#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001618 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001619 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001620 abort();
1621 }
1622#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001623 g_stacknleft += g_stacknxt - (char *)p;
1624 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001625}
1626
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001627/*
1628 * Like strdup but works with the ash stack.
1629 */
1630static char *
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02001631sstrdup(const char *p)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001632{
1633 size_t len = strlen(p) + 1;
1634 return memcpy(stalloc(len), p, len);
1635}
1636
Denys Vlasenko03c36e02018-01-10 15:18:35 +01001637static ALWAYS_INLINE void
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001638grabstackblock(size_t len)
1639{
Denys Vlasenkoa318bba2016-10-26 18:26:27 +02001640 stalloc(len);
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001641}
1642
1643static void
1644pushstackmark(struct stackmark *mark, size_t len)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001645{
Denis Vlasenko01631112007-12-16 17:20:38 +00001646 mark->stackp = g_stackp;
1647 mark->stacknxt = g_stacknxt;
1648 mark->stacknleft = g_stacknleft;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001649 grabstackblock(len);
1650}
1651
1652static void
1653setstackmark(struct stackmark *mark)
1654{
1655 pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001656}
1657
1658static void
1659popstackmark(struct stackmark *mark)
1660{
1661 struct stack_block *sp;
1662
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001663 if (!mark->stackp)
1664 return;
1665
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001666 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001667 while (g_stackp != mark->stackp) {
1668 sp = g_stackp;
1669 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001670 free(sp);
1671 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001672 g_stacknxt = mark->stacknxt;
1673 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001674 sstrend = mark->stacknxt + mark->stacknleft;
1675 INT_ON;
1676}
1677
1678/*
1679 * When the parser reads in a string, it wants to stick the string on the
1680 * stack and only adjust the stack pointer when it knows how big the
1681 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1682 * of space on top of the stack and stackblocklen returns the length of
1683 * this block. Growstackblock will grow this space by at least one byte,
1684 * possibly moving it (like realloc). Grabstackblock actually allocates the
1685 * part of the block that has been used.
1686 */
1687static void
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001688growstackblock(size_t min)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001689{
1690 size_t newlen;
1691
Denis Vlasenko01631112007-12-16 17:20:38 +00001692 newlen = g_stacknleft * 2;
1693 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001694 ash_msg_and_raise_error(bb_msg_memory_exhausted);
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001695 min = SHELL_ALIGN(min | 128);
1696 if (newlen < min)
1697 newlen += min;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001698
Denis Vlasenko01631112007-12-16 17:20:38 +00001699 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001700 struct stack_block *sp;
1701 struct stack_block *prevstackp;
1702 size_t grosslen;
1703
1704 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001705 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001706 prevstackp = sp->prev;
1707 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1708 sp = ckrealloc(sp, grosslen);
1709 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001710 g_stackp = sp;
1711 g_stacknxt = sp->space;
1712 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001713 sstrend = sp->space + newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001714 INT_ON;
1715 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001716 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001717 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001718 char *p = stalloc(newlen);
1719
1720 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001721 g_stacknxt = memcpy(p, oldspace, oldlen);
1722 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001723 }
1724}
1725
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001726/*
1727 * The following routines are somewhat easier to use than the above.
1728 * The user declares a variable of type STACKSTR, which may be declared
1729 * to be a register. The macro STARTSTACKSTR initializes things. Then
1730 * the user uses the macro STPUTC to add characters to the string. In
1731 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1732 * grown as necessary. When the user is done, she can just leave the
1733 * string there and refer to it using stackblock(). Or she can allocate
1734 * the space for it using grabstackstr(). If it is necessary to allow
1735 * someone else to use the stack temporarily and then continue to grow
1736 * the string, the user should use grabstack to allocate the space, and
1737 * then call ungrabstr(p) to return to the previous mode of operation.
1738 *
1739 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1740 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1741 * is space for at least one character.
1742 */
1743static void *
1744growstackstr(void)
1745{
1746 size_t len = stackblocksize();
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001747 growstackblock(0);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001748 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001749}
1750
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001751static char *
1752growstackto(size_t len)
1753{
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001754 if (stackblocksize() < len)
1755 growstackblock(len);
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001756 return stackblock();
1757}
1758
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001759/*
1760 * Called from CHECKSTRSPACE.
1761 */
1762static char *
1763makestrspace(size_t newlen, char *p)
1764{
Denis Vlasenko01631112007-12-16 17:20:38 +00001765 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001766
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001767 return growstackto(len + newlen) + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001768}
1769
1770static char *
Denys Vlasenko538ee412020-02-22 19:11:41 +01001771stnputs(const char *s, size_t n, char *p)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001772{
1773 p = makestrspace(n, p);
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001774 p = (char *)mempcpy(p, s, n);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001775 return p;
1776}
1777
1778static char *
1779stack_putstr(const char *s, char *p)
1780{
Denys Vlasenko538ee412020-02-22 19:11:41 +01001781 return stnputs(s, strlen(s), p);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001782}
1783
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001784static char *
1785_STPUTC(int c, char *p)
1786{
1787 if (p == sstrend)
1788 p = growstackstr();
1789 *p++ = c;
1790 return p;
1791}
1792
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001793#define STARTSTACKSTR(p) ((p) = stackblock())
1794#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001795#define CHECKSTRSPACE(n, p) do { \
1796 char *q = (p); \
1797 size_t l = (n); \
1798 size_t m = sstrend - q; \
1799 if (l > m) \
1800 (p) = makestrspace(l, q); \
1801} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001802#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001803#define STACKSTRNUL(p) do { \
1804 if ((p) == sstrend) \
1805 (p) = growstackstr(); \
1806 *(p) = '\0'; \
1807} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001808#define STUNPUTC(p) (--(p))
1809#define STTOPC(p) ((p)[-1])
1810#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001811
1812#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001813#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001814#define stackstrend() ((void *)sstrend)
1815
1816
1817/* ============ String helpers */
1818
1819/*
1820 * prefix -- see if pfx is a prefix of string.
1821 */
1822static char *
1823prefix(const char *string, const char *pfx)
1824{
1825 while (*pfx) {
1826 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001827 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001828 }
1829 return (char *) string;
1830}
1831
1832/*
1833 * Check for a valid number. This should be elsewhere.
1834 */
1835static int
1836is_number(const char *p)
1837{
1838 do {
1839 if (!isdigit(*p))
1840 return 0;
1841 } while (*++p != '\0');
1842 return 1;
1843}
1844
1845/*
1846 * Convert a string of digits to an integer, printing an error message on
1847 * failure.
1848 */
1849static int
1850number(const char *s)
1851{
1852 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001853 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001854 return atoi(s);
1855}
1856
1857/*
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001858 * Produce a single quoted string suitable as input to the shell.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001859 * The return string is allocated on the stack.
1860 */
1861static char *
1862single_quote(const char *s)
1863{
1864 char *p;
1865
1866 STARTSTACKSTR(p);
1867
1868 do {
1869 char *q;
1870 size_t len;
1871
1872 len = strchrnul(s, '\'') - s;
1873
1874 q = p = makestrspace(len + 3, p);
1875
1876 *q++ = '\'';
Denys Vlasenko94af83e2017-07-23 21:55:40 +02001877 q = (char *)mempcpy(q, s, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001878 *q++ = '\'';
1879 s += len;
1880
1881 STADJUST(q - p, p);
1882
Denys Vlasenkocd716832009-11-28 22:14:02 +01001883 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001884 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001885 len = 0;
1886 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001887
1888 q = p = makestrspace(len + 3, p);
1889
1890 *q++ = '"';
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001891 q = (char *)mempcpy(q, s - len, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001892 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001893
1894 STADJUST(q - p, p);
1895 } while (*s);
1896
Denys Vlasenkocd716832009-11-28 22:14:02 +01001897 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001898
1899 return stackblock();
1900}
1901
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001902/*
1903 * Produce a possibly single quoted string suitable as input to the shell.
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001904 * If quoting was done, the return string is allocated on the stack,
1905 * otherwise a pointer to the original string is returned.
1906 */
1907static const char *
1908maybe_single_quote(const char *s)
1909{
1910 const char *p = s;
1911
1912 while (*p) {
1913 /* Assuming ACSII */
1914 /* quote ctrl_chars space !"#$%&'()* */
1915 if (*p < '+')
1916 goto need_quoting;
1917 /* quote ;<=>? */
1918 if (*p >= ';' && *p <= '?')
1919 goto need_quoting;
1920 /* quote `[\ */
1921 if (*p == '`')
1922 goto need_quoting;
1923 if (*p == '[')
1924 goto need_quoting;
1925 if (*p == '\\')
1926 goto need_quoting;
1927 /* quote {|}~ DEL and high bytes */
1928 if (*p > 'z')
1929 goto need_quoting;
1930 /* Not quoting these: +,-./ 0-9 :@ A-Z ]^_ a-z */
1931 /* TODO: maybe avoid quoting % */
1932 p++;
1933 }
1934 return s;
1935
1936 need_quoting:
1937 return single_quote(s);
1938}
1939
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001940
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001941/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001942
1943static char **argptr; /* argument list for builtin commands */
1944static char *optionarg; /* set by nextopt (like getopt) */
1945static char *optptr; /* used by nextopt */
1946
1947/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001948 * XXX - should get rid of. Have all builtins use getopt(3).
1949 * The library getopt must have the BSD extension static variable
1950 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001951 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001952 * Standard option processing (a la getopt) for builtin routines.
1953 * The only argument that is passed to nextopt is the option string;
1954 * the other arguments are unnecessary. It returns the character,
1955 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001956 */
1957static int
1958nextopt(const char *optstring)
1959{
1960 char *p;
1961 const char *q;
1962 char c;
1963
1964 p = optptr;
1965 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001966 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001967 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001968 if (p == NULL)
1969 return '\0';
1970 if (*p != '-')
1971 return '\0';
1972 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001973 return '\0';
1974 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001975 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001976 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001977 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001978 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001979 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001980 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001981 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001982 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001983 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984 if (*++q == ':')
1985 q++;
1986 }
1987 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001988 if (*p == '\0') {
1989 p = *argptr++;
1990 if (p == NULL)
1991 ash_msg_and_raise_error("no arg for -%c option", c);
1992 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001993 optionarg = p;
1994 p = NULL;
1995 }
1996 optptr = p;
1997 return c;
1998}
1999
2000
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002001/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002002
Denis Vlasenko01631112007-12-16 17:20:38 +00002003struct shparam {
2004 int nparam; /* # of positional parameters (without $0) */
2005#if ENABLE_ASH_GETOPTS
2006 int optind; /* next parameter to be processed by getopts */
2007 int optoff; /* used by getopts */
2008#endif
2009 unsigned char malloced; /* if parameter list dynamically allocated */
2010 char **p; /* parameter list */
2011};
2012
2013/*
2014 * Free the list of positional parameters.
2015 */
2016static void
2017freeparam(volatile struct shparam *param)
2018{
Denis Vlasenko01631112007-12-16 17:20:38 +00002019 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00002020 char **ap, **ap1;
2021 ap = ap1 = param->p;
2022 while (*ap)
2023 free(*ap++);
2024 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00002025 }
2026}
2027
2028#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002029static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00002030#endif
2031
2032struct var {
2033 struct var *next; /* next entry in hash list */
2034 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002035 const char *var_text; /* name=value */
2036 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00002037 /* the variable gets set/unset */
2038};
2039
2040struct localvar {
2041 struct localvar *next; /* next local variable in list */
2042 struct var *vp; /* the variable that was made local */
2043 int flags; /* saved flags */
2044 const char *text; /* saved text */
2045};
2046
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002047/* flags */
2048#define VEXPORT 0x01 /* variable is exported */
2049#define VREADONLY 0x02 /* variable cannot be modified */
2050#define VSTRFIXED 0x04 /* variable struct is statically allocated */
2051#define VTEXTFIXED 0x08 /* text is statically allocated */
2052#define VSTACK 0x10 /* text is allocated on the stack */
2053#define VUNSET 0x20 /* the variable is not set */
2054#define VNOFUNC 0x40 /* don't call the callback function */
2055#define VNOSET 0x80 /* do not set variable - just readonly test */
2056#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002057#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002058# define VDYNAMIC 0x200 /* dynamic variable */
2059#else
2060# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002061#endif
2062
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002063
Denis Vlasenko01631112007-12-16 17:20:38 +00002064/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002065#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002066static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002067change_lc_all(const char *value)
2068{
2069 if (value && *value != '\0')
2070 setlocale(LC_ALL, value);
2071}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002072static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002073change_lc_ctype(const char *value)
2074{
2075 if (value && *value != '\0')
2076 setlocale(LC_CTYPE, value);
2077}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002078#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079#if ENABLE_ASH_MAIL
2080static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01002081static void changemail(const char *var_value) FAST_FUNC;
2082#else
2083# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002084#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002085static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002086#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002087static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002088#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002089#if BASH_EPOCH_VARS
2090static void change_seconds(const char *) FAST_FUNC;
2091static void change_realtime(const char *) FAST_FUNC;
2092#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002093
Denis Vlasenko01631112007-12-16 17:20:38 +00002094static const struct {
2095 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002096 const char *var_text;
2097 void (*var_func)(const char *) FAST_FUNC;
Denys Vlasenko965b7952020-11-30 13:03:03 +01002098} varinit_data[] ALIGN_PTR = {
Denys Vlasenko566a3132012-07-07 21:40:35 +02002099 /*
2100 * Note: VEXPORT would not work correctly here for NOFORK applets:
2101 * some environment strings may be constant.
2102 */
Denis Vlasenko01631112007-12-16 17:20:38 +00002103 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002104#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002105 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
2106 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002107#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00002108 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
2109 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
2110 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
2111 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112#if ENABLE_ASH_GETOPTS
Denys Vlasenkoe627ac92016-09-30 14:36:59 +02002113 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002114#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002115 { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002116#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002117 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002118#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002119#if BASH_EPOCH_VARS
2120 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds },
2121 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime },
2122#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002123#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002124 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
2125 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002126#endif
2127#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002128 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002129#endif
2130};
2131
Denis Vlasenko0b769642008-07-24 07:54:57 +00002132struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00002133
2134struct globals_var {
2135 struct shparam shellparam; /* $@ current positional parameters */
2136 struct redirtab *redirlist;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02002137 int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */
Denis Vlasenko01631112007-12-16 17:20:38 +00002138 struct var *vartab[VTABSIZE];
2139 struct var varinit[ARRAY_SIZE(varinit_data)];
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002140 int lineno;
2141 char linenovar[sizeof("LINENO=") + sizeof(int)*3];
Denis Vlasenko01631112007-12-16 17:20:38 +00002142};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01002143extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00002144#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00002145#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00002146//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00002147#define preverrout_fd (G_var.preverrout_fd)
2148#define vartab (G_var.vartab )
2149#define varinit (G_var.varinit )
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002150#define lineno (G_var.lineno )
2151#define linenovar (G_var.linenovar )
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002152#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002153#if ENABLE_ASH_MAIL
Ron Yorston1d371862019-04-15 10:52:05 +01002154# define vmail varinit[1]
2155# define vmpath varinit[2]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002156#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002157#define VAR_OFFSET1 (ENABLE_ASH_MAIL*2)
2158#define vpath varinit[VAR_OFFSET1 + 1]
2159#define vps1 varinit[VAR_OFFSET1 + 2]
2160#define vps2 varinit[VAR_OFFSET1 + 3]
2161#define vps4 varinit[VAR_OFFSET1 + 4]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002162#if ENABLE_ASH_GETOPTS
Ron Yorston1d371862019-04-15 10:52:05 +01002163# define voptind varinit[VAR_OFFSET1 + 5]
2164#endif
2165#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS)
2166#define vlineno varinit[VAR_OFFSET2 + 5]
2167#if ENABLE_ASH_RANDOM_SUPPORT
2168# define vrandom varinit[VAR_OFFSET2 + 6]
2169#endif
2170#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT)
2171#if BASH_EPOCH_VARS
2172# define vepochs varinit[VAR_OFFSET3 + 6]
2173# define vepochr varinit[VAR_OFFSET3 + 7]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002174#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002175#define INIT_G_var() do { \
2176 unsigned i; \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +02002177 (*(struct globals_var**)not_const_pp(&ash_ptr_to_globals_var)) = xzalloc(sizeof(G_var)); \
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002178 barrier(); \
2179 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
2180 varinit[i].flags = varinit_data[i].flags; \
2181 varinit[i].var_text = varinit_data[i].var_text; \
2182 varinit[i].var_func = varinit_data[i].var_func; \
2183 } \
2184 strcpy(linenovar, "LINENO="); \
2185 vlineno.var_text = linenovar; \
2186} while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002187
2188/*
2189 * The following macros access the values of the above variables.
2190 * They have to skip over the name. They return the null string
2191 * for unset variables.
2192 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002193#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002194#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002195#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002196# define mailval() (vmail.var_text + 5)
2197# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002198# define mpathset() ((vmpath.flags & VUNSET) == 0)
2199#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002200#define pathval() (vpath.var_text + 5)
2201#define ps1val() (vps1.var_text + 4)
2202#define ps2val() (vps2.var_text + 4)
2203#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002204#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002205# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002206#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002207
Denis Vlasenko01631112007-12-16 17:20:38 +00002208#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002209static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002210getoptsreset(const char *value)
2211{
Denys Vlasenko46289452017-08-11 00:59:36 +02002212 shellparam.optind = 1;
2213 if (is_number(value))
2214 shellparam.optind = number(value) ?: 1;
Denis Vlasenko01631112007-12-16 17:20:38 +00002215 shellparam.optoff = -1;
2216}
2217#endif
2218
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002219/*
2220 * Compares two strings up to the first = or '\0'. The first
2221 * string must be terminated by '='; the second may be terminated by
2222 * either '=' or '\0'.
2223 */
2224static int
2225varcmp(const char *p, const char *q)
2226{
2227 int c, d;
2228
2229 while ((c = *p) == (d = *q)) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002230 if (c == '\0' || c == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231 goto out;
2232 p++;
2233 q++;
2234 }
2235 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002236 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002237 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002238 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002239 out:
2240 return c - d;
2241}
2242
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002243/*
2244 * Find the appropriate entry in the hash table from the name.
2245 */
2246static struct var **
2247hashvar(const char *p)
2248{
2249 unsigned hashval;
2250
2251 hashval = ((unsigned char) *p) << 4;
2252 while (*p && *p != '=')
2253 hashval += (unsigned char) *p++;
2254 return &vartab[hashval % VTABSIZE];
2255}
2256
2257static int
2258vpcmp(const void *a, const void *b)
2259{
2260 return varcmp(*(const char **)a, *(const char **)b);
2261}
2262
2263/*
2264 * This routine initializes the builtin variables.
2265 */
2266static void
2267initvar(void)
2268{
2269 struct var *vp;
2270 struct var *end;
2271 struct var **vpp;
2272
2273 /*
2274 * PS1 depends on uid
2275 */
2276#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002277 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002278#else
2279 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002280 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002281#endif
2282 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002283 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002284 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002285 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002286 vp->next = *vpp;
2287 *vpp = vp;
2288 } while (++vp < end);
2289}
2290
2291static struct var **
2292findvar(struct var **vpp, const char *name)
2293{
2294 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002295 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002296 break;
2297 }
2298 }
2299 return vpp;
2300}
2301
2302/*
2303 * Find the value of a variable. Returns NULL if not set.
2304 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002305static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002306lookupvar(const char *name)
2307{
2308 struct var *v;
2309
2310 v = *findvar(hashvar(name), name);
2311 if (v) {
Ron Yorston1d371862019-04-15 10:52:05 +01002312#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002313 /*
2314 * Dynamic variables are implemented roughly the same way they are
2315 * in bash. Namely, they're "special" so long as they aren't unset.
2316 * As soon as they're unset, they're no longer dynamic, and dynamic
2317 * lookup will no longer happen at that point. -- PFM.
2318 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002319 if (v->flags & VDYNAMIC)
2320 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002321#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002322 if (!(v->flags & VUNSET)) {
2323 if (v == &vlineno && v->var_text == linenovar) {
2324 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
2325 }
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002326 return var_end(v->var_text);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002327 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002328 }
2329 return NULL;
2330}
2331
Denys Vlasenko0b883582016-12-23 16:49:07 +01002332#if ENABLE_UNICODE_SUPPORT
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02002333static void
2334reinit_unicode_for_ash(void)
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002335{
2336 /* Unicode support should be activated even if LANG is set
2337 * _during_ shell execution, not only if it was set when
2338 * shell was started. Therefore, re-check LANG every time:
2339 */
2340 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2341 || ENABLE_UNICODE_USING_LOCALE
2342 ) {
2343 const char *s = lookupvar("LC_ALL");
2344 if (!s) s = lookupvar("LC_CTYPE");
2345 if (!s) s = lookupvar("LANG");
2346 reinit_unicode(s);
2347 }
2348}
Denys Vlasenko0b883582016-12-23 16:49:07 +01002349#else
2350# define reinit_unicode_for_ash() ((void)0)
2351#endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002352
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002353/*
2354 * Search the environment of a builtin command.
2355 */
Denys Vlasenko488e6092017-07-26 23:08:36 +02002356static ALWAYS_INLINE const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002357bltinlookup(const char *name)
2358{
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002359 return lookupvar(name);
2360}
2361
2362/*
2363 * Same as setvar except that the variable and value are passed in
2364 * the first argument as name=value. Since the first argument will
2365 * be actually stored in the table, it should not be a string that
2366 * will go away.
2367 * Called with interrupts off.
2368 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002369static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002370setvareq(char *s, int flags)
2371{
2372 struct var *vp, **vpp;
2373
2374 vpp = hashvar(s);
2375 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002376 vpp = findvar(vpp, s);
2377 vp = *vpp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002378 if (vp) {
2379 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2380 const char *n;
2381
2382 if (flags & VNOSAVE)
2383 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002384 n = vp->var_text;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +02002385 exitstatus = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002386 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2387 }
2388
2389 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002390 goto out;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002391
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002392 if (vp->var_func && !(flags & VNOFUNC))
2393 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002394
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002395 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2396 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002397
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002398 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2399 *vpp = vp->next;
2400 free(vp);
2401 out_free:
2402 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2403 free(s);
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002404 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002405 }
2406
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002407 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
Ron Yorston1d371862019-04-15 10:52:05 +01002408#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Ron Yorstond96c69d2019-04-15 10:49:35 +01002409 if (flags & VUNSET)
2410 flags &= ~VDYNAMIC;
2411#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002412 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002413 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002414 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002415 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002416 if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
2417 goto out_free;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002418 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002419 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002420 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002421 *vpp = vp;
2422 }
2423 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2424 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002425 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002426 vp->flags = flags;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002427
2428 out:
2429 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002430}
2431
2432/*
2433 * Set the value of a variable. The flags argument is ored with the
2434 * flags of the variable. If val is NULL, the variable is unset.
2435 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002436static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002437setvar(const char *name, const char *val, int flags)
2438{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002439 const char *q;
2440 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002441 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002442 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002443 size_t vallen;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002444 struct var *vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002445
2446 q = endofname(name);
2447 p = strchrnul(q, '=');
2448 namelen = p - name;
2449 if (!namelen || p != q)
2450 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2451 vallen = 0;
2452 if (val == NULL) {
2453 flags |= VUNSET;
2454 } else {
2455 vallen = strlen(val);
2456 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002457
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002458 INT_OFF;
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002459 nameeq = ckzalloc(namelen + vallen + 2);
Denys Vlasenkoda2244f2017-07-21 18:51:29 +02002460 p = mempcpy(nameeq, name, namelen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002461 if (val) {
2462 *p++ = '=';
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002463 memcpy(p, val, vallen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002464 }
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002465 vp = setvareq(nameeq, flags | VNOSAVE);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002466 INT_ON;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002467
2468 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002469}
2470
Denys Vlasenko03dad222010-01-12 23:29:57 +01002471static void FAST_FUNC
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002472setvar0(const char *name, const char *val)
Denys Vlasenko03dad222010-01-12 23:29:57 +01002473{
2474 setvar(name, val, 0);
2475}
2476
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002477/*
2478 * Unset the specified variable.
2479 */
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002480static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002481unsetvar(const char *s)
2482{
Denys Vlasenkocf3a7962017-07-26 14:38:19 +02002483 setvar(s, NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002484}
2485
2486/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002487 * Generate a list of variables satisfying the given conditions.
2488 */
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002489#if !ENABLE_FEATURE_SH_NOFORK
2490# define listvars(on, off, lp, end) listvars(on, off, end)
2491#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002492static char **
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002493listvars(int on, int off, struct strlist *lp, char ***end)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002494{
2495 struct var **vpp;
2496 struct var *vp;
2497 char **ep;
2498 int mask;
2499
2500 STARTSTACKSTR(ep);
2501 vpp = vartab;
2502 mask = on | off;
2503 do {
2504 for (vp = *vpp; vp; vp = vp->next) {
2505 if ((vp->flags & mask) == on) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002506#if ENABLE_FEATURE_SH_NOFORK
2507 /* If variable with the same name is both
2508 * exported and temporarily set for a command:
2509 * export ZVAR=5
2510 * ZVAR=6 printenv
2511 * then "ZVAR=6" will be both in vartab and
2512 * lp lists. Do not pass it twice to printenv.
2513 */
2514 struct strlist *lp1 = lp;
2515 while (lp1) {
2516 if (strcmp(lp1->text, vp->var_text) == 0)
2517 goto skip;
2518 lp1 = lp1->next;
2519 }
2520#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002521 if (ep == stackstrend())
2522 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002523 *ep++ = (char*)vp->var_text;
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002524#if ENABLE_FEATURE_SH_NOFORK
2525 skip: ;
2526#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002527 }
2528 }
2529 } while (++vpp < vartab + VTABSIZE);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002530
2531#if ENABLE_FEATURE_SH_NOFORK
2532 while (lp) {
2533 if (ep == stackstrend())
2534 ep = growstackstr();
2535 *ep++ = lp->text;
2536 lp = lp->next;
2537 }
2538#endif
2539
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002540 if (ep == stackstrend())
2541 ep = growstackstr();
2542 if (end)
2543 *end = ep;
2544 *ep++ = NULL;
2545 return grabstackstr(ep);
2546}
2547
2548
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002549/* ============ Path search helper */
2550static const char *
2551legal_pathopt(const char *opt, const char *term, int magic)
2552{
2553 switch (magic) {
2554 case 0:
2555 opt = NULL;
2556 break;
2557
2558 case 1:
2559 opt = prefix(opt, "builtin") ?: prefix(opt, "func");
2560 break;
2561
2562 default:
2563 opt += strcspn(opt, term);
2564 break;
2565 }
2566
2567 if (opt && *opt == '%')
2568 opt++;
2569
2570 return opt;
2571}
2572
2573/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002574 * The variable path (passed by reference) should be set to the start
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002575 * of the path before the first call; padvance will update
2576 * this value as it proceeds. Successive calls to padvance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002577 * the possible path expansions in sequence. If an option (indicated by
2578 * a percent sign) appears in the path entry then the global variable
2579 * pathopt will be set to point to it; otherwise pathopt will be set to
2580 * NULL.
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002581 *
2582 * If magic is 0 then pathopt recognition will be disabled. If magic is
2583 * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
2584 * pathopt.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002585 */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002586static const char *pathopt; /* set by padvance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002587
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002588static int
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002589padvance_magic(const char **path, const char *name, int magic)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002590{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002591 const char *term = "%:";
2592 const char *lpathopt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002593 const char *p;
2594 char *q;
2595 const char *start;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002596 size_t qlen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002597 size_t len;
2598
2599 if (*path == NULL)
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002600 return -1;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002601
2602 lpathopt = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002603 start = *path;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002604
2605 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2606 lpathopt = start + 1;
2607 start = p;
2608 term = ":";
2609 }
2610
2611 len = strcspn(start, term);
2612 p = start + len;
2613
2614 if (*p == '%') {
2615 size_t extra = strchrnul(p, ':') - p;
2616
2617 if (legal_pathopt(p + 1, term, magic))
2618 lpathopt = p + 1;
2619 else
2620 len += extra;
2621
2622 p += extra;
2623 }
2624
2625 pathopt = lpathopt;
2626 *path = *p == ':' ? p + 1 : NULL;
2627
2628 /* "2" is for '/' and '\0' */
2629 qlen = len + strlen(name) + 2;
2630 q = growstackto(qlen);
2631
2632 if (len) {
2633 q = mempcpy(q, start, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002634 *q++ = '/';
2635 }
2636 strcpy(q, name);
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002637
2638 return qlen;
2639}
2640
2641static int
2642padvance(const char **path, const char *name)
2643{
2644 return padvance_magic(path, name, 1);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002645}
2646
2647
2648/* ============ Prompt */
2649
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002650static smallint doprompt; /* if set, prompt the user */
2651static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002652
2653#if ENABLE_FEATURE_EDITING
2654static line_input_t *line_input_state;
2655static const char *cmdedit_prompt;
2656static void
2657putprompt(const char *s)
2658{
2659 if (ENABLE_ASH_EXPAND_PRMT) {
2660 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002661 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002662 return;
2663 }
2664 cmdedit_prompt = s;
2665}
2666#else
2667static void
2668putprompt(const char *s)
2669{
2670 out2str(s);
2671}
2672#endif
2673
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002674/* expandstr() needs parsing machinery, so it is far away ahead... */
Denys Vlasenko46999802017-07-29 21:12:29 +02002675static const char *expandstr(const char *ps, int syntax_type);
2676/* Values for syntax param */
2677#define BASESYNTAX 0 /* not in quotes */
2678#define DQSYNTAX 1 /* in double quotes */
2679#define SQSYNTAX 2 /* in single quotes */
2680#define ARISYNTAX 3 /* in arithmetic */
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +02002681#if ENABLE_ASH_EXPAND_PRMT
2682# define PSSYNTAX 4 /* prompt. never passed to SIT() */
2683#endif
Denys Vlasenko46999802017-07-29 21:12:29 +02002684/* PSSYNTAX expansion is identical to DQSYNTAX, except keeping '\$' as '\$' */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002685
Denys Vlasenko46999802017-07-29 21:12:29 +02002686/*
2687 * called by editline -- any expansions to the prompt should be added here.
2688 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002689static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002690setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002691{
2692 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002693 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2694
2695 if (!do_set)
2696 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002697
2698 needprompt = 0;
2699
2700 switch (whichprompt) {
2701 case 1:
2702 prompt = ps1val();
2703 break;
2704 case 2:
2705 prompt = ps2val();
2706 break;
2707 default: /* 0 */
2708 prompt = nullstr;
2709 }
2710#if ENABLE_ASH_EXPAND_PRMT
Denys Vlasenko60ca8342016-09-30 11:21:21 +02002711 pushstackmark(&smark, stackblocksize());
Denys Vlasenko46999802017-07-29 21:12:29 +02002712 putprompt(expandstr(prompt, PSSYNTAX));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002713 popstackmark(&smark);
Denys Vlasenko48c803a2017-07-01 23:24:48 +02002714#else
2715 putprompt(prompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002716#endif
2717}
2718
2719
2720/* ============ The cd and pwd commands */
2721
2722#define CD_PHYSICAL 1
2723#define CD_PRINT 2
2724
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002725static int
2726cdopt(void)
2727{
2728 int flags = 0;
2729 int i, j;
2730
2731 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002732 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002733 if (i != j) {
2734 flags ^= CD_PHYSICAL;
2735 j = i;
2736 }
2737 }
2738
2739 return flags;
2740}
2741
2742/*
2743 * Update curdir (the name of the current directory) in response to a
2744 * cd command.
2745 */
2746static const char *
2747updatepwd(const char *dir)
2748{
2749 char *new;
2750 char *p;
2751 char *cdcomppath;
2752 const char *lim;
2753
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02002754 cdcomppath = sstrdup(dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002755 STARTSTACKSTR(new);
2756 if (*dir != '/') {
2757 if (curdir == nullstr)
2758 return 0;
2759 new = stack_putstr(curdir, new);
2760 }
2761 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002762 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002763 if (*dir != '/') {
2764 if (new[-1] != '/')
2765 USTPUTC('/', new);
2766 if (new > lim && *lim == '/')
2767 lim++;
2768 } else {
2769 USTPUTC('/', new);
2770 cdcomppath++;
2771 if (dir[1] == '/' && dir[2] != '/') {
2772 USTPUTC('/', new);
2773 cdcomppath++;
2774 lim++;
2775 }
2776 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002777 p = strtok_r(cdcomppath, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002778 while (p) {
2779 switch (*p) {
2780 case '.':
2781 if (p[1] == '.' && p[2] == '\0') {
2782 while (new > lim) {
2783 STUNPUTC(new);
2784 if (new[-1] == '/')
2785 break;
2786 }
2787 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002788 }
2789 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002790 break;
2791 /* fall through */
2792 default:
2793 new = stack_putstr(p, new);
2794 USTPUTC('/', new);
2795 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002796 p = strtok_r(NULL, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002797 }
2798 if (new > lim)
2799 STUNPUTC(new);
2800 *new = 0;
2801 return stackblock();
2802}
2803
2804/*
2805 * Find out what the current directory is. If we already know the current
2806 * directory, this routine returns immediately.
2807 */
2808static char *
2809getpwd(void)
2810{
Denis Vlasenko01631112007-12-16 17:20:38 +00002811 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002812 return dir ? dir : nullstr;
2813}
2814
2815static void
2816setpwd(const char *val, int setold)
2817{
2818 char *oldcur, *dir;
2819
2820 oldcur = dir = curdir;
2821
2822 if (setold) {
2823 setvar("OLDPWD", oldcur, VEXPORT);
2824 }
2825 INT_OFF;
2826 if (physdir != nullstr) {
2827 if (physdir != oldcur)
2828 free(physdir);
2829 physdir = nullstr;
2830 }
2831 if (oldcur == val || !val) {
2832 char *s = getpwd();
2833 physdir = s;
2834 if (!val)
2835 dir = s;
2836 } else
2837 dir = ckstrdup(val);
2838 if (oldcur != dir && oldcur != nullstr) {
2839 free(oldcur);
2840 }
2841 curdir = dir;
2842 INT_ON;
2843 setvar("PWD", dir, VEXPORT);
2844}
2845
2846static void hashcd(void);
2847
2848/*
Denys Vlasenko70392332016-10-27 02:31:55 +02002849 * Actually do the chdir. We also call hashcd to let other routines
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002850 * know that the current directory has changed.
2851 */
2852static int
2853docd(const char *dest, int flags)
2854{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002855 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002856 int err;
2857
2858 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2859
2860 INT_OFF;
2861 if (!(flags & CD_PHYSICAL)) {
2862 dir = updatepwd(dest);
2863 if (dir)
2864 dest = dir;
2865 }
2866 err = chdir(dest);
2867 if (err)
2868 goto out;
2869 setpwd(dir, 1);
2870 hashcd();
2871 out:
2872 INT_ON;
2873 return err;
2874}
2875
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002876static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002877cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002878{
2879 const char *dest;
2880 const char *path;
2881 const char *p;
2882 char c;
2883 struct stat statb;
2884 int flags;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002885 int len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002886
2887 flags = cdopt();
2888 dest = *argptr;
2889 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002890 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002891 else if (LONE_DASH(dest)) {
2892 dest = bltinlookup("OLDPWD");
2893 flags |= CD_PRINT;
2894 }
2895 if (!dest)
2896 dest = nullstr;
2897 if (*dest == '/')
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002898 goto step6;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002899 if (*dest == '.') {
2900 c = dest[1];
2901 dotdot:
2902 switch (c) {
2903 case '\0':
2904 case '/':
2905 goto step6;
2906 case '.':
2907 c = dest[2];
2908 if (c != '.')
2909 goto dotdot;
2910 }
2911 }
2912 if (!*dest)
2913 dest = ".";
2914 path = bltinlookup("CDPATH");
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002915 while (p = path, (len = padvance(&path, dest)) >= 0) {
2916 c = *p;
2917 p = stalloc(len);
2918
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002919 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2920 if (c && c != ':')
2921 flags |= CD_PRINT;
2922 docd:
2923 if (!docd(p, flags))
2924 goto out;
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002925 goto err;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002926 }
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002927 }
2928
2929 step6:
2930 p = dest;
2931 goto docd;
2932
2933 err:
Johannes Schindelin687aac02017-08-22 22:03:22 +02002934 ash_msg_and_raise_perror("can't cd to %s", dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002935 /* NOTREACHED */
2936 out:
2937 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002938 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002939 return 0;
2940}
2941
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002942static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002943pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002944{
2945 int flags;
2946 const char *dir = curdir;
2947
2948 flags = cdopt();
2949 if (flags) {
2950 if (physdir == nullstr)
2951 setpwd(dir, 0);
2952 dir = physdir;
2953 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002954 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002955 return 0;
2956}
2957
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002958
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002959/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002960
Denis Vlasenko834dee72008-10-07 09:18:30 +00002961
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002962#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002963
Eric Andersenc470f442003-07-28 09:56:35 +00002964/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002965#define CWORD 0 /* character is nothing special */
2966#define CNL 1 /* newline character */
2967#define CBACK 2 /* a backslash character */
2968#define CSQUOTE 3 /* single quote */
2969#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002970#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002971#define CBQUOTE 6 /* backwards single quote */
2972#define CVAR 7 /* a dollar sign */
2973#define CENDVAR 8 /* a '}' character */
2974#define CLP 9 /* a left paren in arithmetic */
2975#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002976#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002977#define CCTL 12 /* like CWORD, except it must be escaped */
2978#define CSPCL 13 /* these terminate a word */
2979#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002980
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002981#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002982#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002983# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002984#endif
2985
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002986#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002987
Denys Vlasenko0b883582016-12-23 16:49:07 +01002988#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002989# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002990#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002991# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002992#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02002993static const uint16_t S_I_T[] ALIGN2 = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002994#if ENABLE_ASH_ALIAS
2995 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2996#endif
2997 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2998 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2999 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
3000 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
3001 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
3002 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
3003 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
3004 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
3005 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
3006 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
3007 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003008#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003009 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
3010 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
3011 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
3012#endif
3013#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00003014};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003015/* Constants below must match table above */
3016enum {
3017#if ENABLE_ASH_ALIAS
3018 CSPCL_CIGN_CIGN_CIGN , /* 0 */
3019#endif
3020 CSPCL_CWORD_CWORD_CWORD , /* 1 */
3021 CNL_CNL_CNL_CNL , /* 2 */
3022 CWORD_CCTL_CCTL_CWORD , /* 3 */
3023 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
3024 CVAR_CVAR_CWORD_CVAR , /* 5 */
3025 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
3026 CSPCL_CWORD_CWORD_CLP , /* 7 */
3027 CSPCL_CWORD_CWORD_CRP , /* 8 */
3028 CBACK_CBACK_CCTL_CBACK , /* 9 */
3029 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
3030 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
3031 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
3032 CWORD_CWORD_CWORD_CWORD , /* 13 */
3033 CCTL_CCTL_CCTL_CCTL , /* 14 */
3034};
Eric Andersen2870d962001-07-02 17:27:21 +00003035
Denys Vlasenkocd716832009-11-28 22:14:02 +01003036/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
3037 * caller must ensure proper cast on it if c is *char_ptr!
3038 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003039#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003040
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003041static int
3042SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003043{
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003044 /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */
3045 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~";
3046 /*
3047 * This causes '/' to be prepended with CTLESC in dquoted string,
3048 * making "./file"* treated incorrectly because we feed
3049 * ".\/file*" string to glob(), confusing it (see expandmeta func).
3050 * The "homegrown" glob implementation is okay with that,
3051 * but glibc one isn't. With '/' always treated as CWORD,
3052 * both work fine.
3053 */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003054# if ENABLE_ASH_ALIAS
3055 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003056 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003057 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003058 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
3059 11, 3 /* "}~" */
3060 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01003061# else
3062 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003063 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003064 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003065 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
3066 10, 2 /* "}~" */
3067 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01003068# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003069 const char *s;
3070 int indx;
3071
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003072 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003073 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01003074# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003075 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003076 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003077 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01003078# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003079 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003080 /* Cast is purely for paranoia here,
3081 * just in case someone passed signed char to us */
3082 if ((unsigned char)c >= CTL_FIRST
3083 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003084 ) {
3085 return CCTL;
3086 }
3087 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003088 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003089 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003090 indx = syntax_index_table[s - spec_symbls];
3091 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003092 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003093}
3094
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003095#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003096
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003097static const uint8_t syntax_index_table[] ALIGN1 = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003098 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003099 /* 0 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 1 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 2 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 3 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 4 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 5 */ CWORD_CWORD_CWORD_CWORD,
3105 /* 6 */ CWORD_CWORD_CWORD_CWORD,
3106 /* 7 */ CWORD_CWORD_CWORD_CWORD,
3107 /* 8 */ CWORD_CWORD_CWORD_CWORD,
3108 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
3109 /* 10 "\n" */ CNL_CNL_CNL_CNL,
3110 /* 11 */ CWORD_CWORD_CWORD_CWORD,
3111 /* 12 */ CWORD_CWORD_CWORD_CWORD,
3112 /* 13 */ CWORD_CWORD_CWORD_CWORD,
3113 /* 14 */ CWORD_CWORD_CWORD_CWORD,
3114 /* 15 */ CWORD_CWORD_CWORD_CWORD,
3115 /* 16 */ CWORD_CWORD_CWORD_CWORD,
3116 /* 17 */ CWORD_CWORD_CWORD_CWORD,
3117 /* 18 */ CWORD_CWORD_CWORD_CWORD,
3118 /* 19 */ CWORD_CWORD_CWORD_CWORD,
3119 /* 20 */ CWORD_CWORD_CWORD_CWORD,
3120 /* 21 */ CWORD_CWORD_CWORD_CWORD,
3121 /* 22 */ CWORD_CWORD_CWORD_CWORD,
3122 /* 23 */ CWORD_CWORD_CWORD_CWORD,
3123 /* 24 */ CWORD_CWORD_CWORD_CWORD,
3124 /* 25 */ CWORD_CWORD_CWORD_CWORD,
3125 /* 26 */ CWORD_CWORD_CWORD_CWORD,
3126 /* 27 */ CWORD_CWORD_CWORD_CWORD,
3127 /* 28 */ CWORD_CWORD_CWORD_CWORD,
3128 /* 29 */ CWORD_CWORD_CWORD_CWORD,
3129 /* 30 */ CWORD_CWORD_CWORD_CWORD,
3130 /* 31 */ CWORD_CWORD_CWORD_CWORD,
3131 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
3132 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
3133 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
3134 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
3135 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
3136 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
3137 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
3138 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
3139 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
3140 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
3141 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
3142 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
3143 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
3144 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
3145 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003146/* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */
3147 /* 47 "/" */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003148 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
3149 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
3150 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
3151 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
3152 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
3153 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
3154 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
3155 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
3156 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
3157 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
3158 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
3159 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
3160 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
3161 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
3162 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
3163 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
3164 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
3165 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
3166 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
3167 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
3168 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
3169 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
3170 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
3171 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
3172 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
3173 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
3174 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
3175 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
3176 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
3177 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
3178 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
3179 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
3180 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
3181 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
3182 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
3183 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
3184 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
3185 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
3186 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
3187 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
3188 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
3189 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
3190 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
3191 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
3192 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
3193 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
3194 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
3195 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
3196 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
3197 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
3198 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
3199 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
3200 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
3201 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
3202 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
3203 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
3204 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
3205 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
3206 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
3207 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
3208 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
3209 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3210 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3211 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3212 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3213 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3214 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3215 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3216 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3217 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3218 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3219 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3220 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3221 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3222 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3223 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3224 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3225 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3226 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3227 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
3228 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
3229 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
3230 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
3231 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
3232 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
3233 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
3234 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
3235 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
3236 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
3237 /* 137 */ CWORD_CWORD_CWORD_CWORD,
3238 /* 138 */ CWORD_CWORD_CWORD_CWORD,
3239 /* 139 */ CWORD_CWORD_CWORD_CWORD,
3240 /* 140 */ CWORD_CWORD_CWORD_CWORD,
3241 /* 141 */ CWORD_CWORD_CWORD_CWORD,
3242 /* 142 */ CWORD_CWORD_CWORD_CWORD,
3243 /* 143 */ CWORD_CWORD_CWORD_CWORD,
3244 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3245 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3246 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3247 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3248 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3249 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3250 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3251 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3252 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3253 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3254 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3255 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3256 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3257 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3258 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3259 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3260 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3261 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3262 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3263 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3264 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3265 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3266 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3267 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3268 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3269 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3270 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3271 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3272 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3273 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3274 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3275 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3276 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3277 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3278 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3279 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3280 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3281 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3282 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3283 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3284 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3285 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3286 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3287 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3288 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3289 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3290 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3291 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3292 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3293 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3294 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3295 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3296 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3297 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3298 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3299 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3300 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3301 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3302 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3303 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3304 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3305 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3306 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3307 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3308 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3309 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3310 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3311 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3312 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3313 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3314 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3315 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3316 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3317 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3318 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3319 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3320 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3321 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3322 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3323 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3324 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3325 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3326 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3327 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3328 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3329 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3330 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3331 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3332 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3333 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3334 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3335 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3336 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3337 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3338 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3339 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3340 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3341 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3342 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3343 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3344 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3345 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3346 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3347 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3348 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3349 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3350 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3351 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3352 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3353 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3354 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3355 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003356 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003357# if ENABLE_ASH_ALIAS
3358 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3359# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003360};
3361
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003362#if 1
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003363# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003364#else /* debug version, caught one signed char bug */
3365# define SIT(c, syntax) \
3366 ({ \
3367 if ((c) < 0 || (c) > (PEOF + ENABLE_ASH_ALIAS)) \
3368 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
Denys Vlasenko0b883582016-12-23 16:49:07 +01003369 if ((syntax) < 0 || (syntax) > (2 + ENABLE_FEATURE_SH_MATH)) \
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003370 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
3371 ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf); \
3372 })
3373#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003374
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003375#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003376
Eric Andersen2870d962001-07-02 17:27:21 +00003377
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003378/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003379
Denis Vlasenko131ae172007-02-18 13:00:19 +00003380#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003381
3382#define ALIASINUSE 1
3383#define ALIASDEAD 2
3384
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003385struct alias {
3386 struct alias *next;
3387 char *name;
3388 char *val;
3389 int flag;
3390};
3391
Denis Vlasenko01631112007-12-16 17:20:38 +00003392
3393static struct alias **atab; // [ATABSIZE];
3394#define INIT_G_alias() do { \
3395 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3396} while (0)
3397
Eric Andersen2870d962001-07-02 17:27:21 +00003398
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003399static struct alias **
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02003400__lookupalias(const char *name)
3401{
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003402 unsigned int hashval;
3403 struct alias **app;
3404 const char *p;
3405 unsigned int ch;
3406
3407 p = name;
3408
3409 ch = (unsigned char)*p;
3410 hashval = ch << 4;
3411 while (ch) {
3412 hashval += ch;
3413 ch = (unsigned char)*++p;
3414 }
3415 app = &atab[hashval % ATABSIZE];
3416
3417 for (; *app; app = &(*app)->next) {
3418 if (strcmp(name, (*app)->name) == 0) {
3419 break;
3420 }
3421 }
3422
3423 return app;
3424}
3425
3426static struct alias *
3427lookupalias(const char *name, int check)
3428{
3429 struct alias *ap = *__lookupalias(name);
3430
3431 if (check && ap && (ap->flag & ALIASINUSE))
3432 return NULL;
3433 return ap;
3434}
3435
3436static struct alias *
3437freealias(struct alias *ap)
3438{
3439 struct alias *next;
3440
3441 if (ap->flag & ALIASINUSE) {
3442 ap->flag |= ALIASDEAD;
3443 return ap;
3444 }
3445
3446 next = ap->next;
3447 free(ap->name);
3448 free(ap->val);
3449 free(ap);
3450 return next;
3451}
Eric Andersencb57d552001-06-28 07:25:16 +00003452
Eric Andersenc470f442003-07-28 09:56:35 +00003453static void
3454setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003455{
3456 struct alias *ap, **app;
3457
3458 app = __lookupalias(name);
3459 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003460 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003461 if (ap) {
3462 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003463 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003464 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003465 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003466 ap->flag &= ~ALIASDEAD;
3467 } else {
3468 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003469 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003470 ap->name = ckstrdup(name);
3471 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003472 /*ap->flag = 0; - ckzalloc did it */
3473 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003474 *app = ap;
3475 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003476 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003477}
3478
Eric Andersenc470f442003-07-28 09:56:35 +00003479static int
3480unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003481{
Eric Andersencb57d552001-06-28 07:25:16 +00003482 struct alias **app;
3483
3484 app = __lookupalias(name);
3485
3486 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003487 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003488 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003489 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003490 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003491 }
3492
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003493 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003494}
3495
Eric Andersenc470f442003-07-28 09:56:35 +00003496static void
3497rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003498{
Eric Andersencb57d552001-06-28 07:25:16 +00003499 struct alias *ap, **app;
3500 int i;
3501
Denis Vlasenkob012b102007-02-19 22:43:01 +00003502 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003503 for (i = 0; i < ATABSIZE; i++) {
3504 app = &atab[i];
3505 for (ap = *app; ap; ap = *app) {
3506 *app = freealias(*app);
3507 if (ap == *app) {
3508 app = &ap->next;
3509 }
3510 }
3511 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003512 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003513}
3514
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003515static void
3516printalias(const struct alias *ap)
3517{
3518 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3519}
3520
Eric Andersencb57d552001-06-28 07:25:16 +00003521/*
3522 * TODO - sort output
3523 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003524static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003525aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003526{
3527 char *n, *v;
3528 int ret = 0;
3529 struct alias *ap;
3530
Denis Vlasenko68404f12008-03-17 09:00:54 +00003531 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003532 int i;
3533
Denis Vlasenko68404f12008-03-17 09:00:54 +00003534 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003535 for (ap = atab[i]; ap; ap = ap->next) {
3536 printalias(ap);
3537 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003538 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003539 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003540 }
3541 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003542 v = strchr(n+1, '=');
3543 if (v == NULL) { /* n+1: funny ksh stuff */
3544 ap = *__lookupalias(n);
3545 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003546 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003547 ret = 1;
3548 } else
3549 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003550 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003551 *v++ = '\0';
3552 setalias(n, v);
3553 }
3554 }
3555
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003556 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003557}
3558
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003559static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003560unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003561{
3562 int i;
3563
Denys Vlasenko6c149f42017-04-12 21:31:32 +02003564 while (nextopt("a") != '\0') {
3565 rmaliases();
3566 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003567 }
3568 for (i = 0; *argptr; argptr++) {
3569 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003570 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003571 i = 1;
3572 }
3573 }
3574
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003575 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003576}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003577
Denis Vlasenko131ae172007-02-18 13:00:19 +00003578#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003579
Eric Andersenc470f442003-07-28 09:56:35 +00003580
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003581/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003582#define FORK_FG 0
3583#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003584#define FORK_NOJOB 2
3585
3586/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003587#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3588#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3589#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denys Vlasenko9c541002015-10-07 15:44:36 +02003590#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003591
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003592/*
3593 * A job structure contains information about a job. A job is either a
3594 * single process or a set of processes contained in a pipeline. In the
3595 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3596 * array of pids.
3597 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003598struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003599 pid_t ps_pid; /* process id */
3600 int ps_status; /* last process status from wait() */
3601 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003602};
3603
3604struct job {
3605 struct procstat ps0; /* status of process */
Denys Vlasenko966f0872019-03-27 15:51:42 +01003606 struct procstat *ps; /* status of processes when more than one */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003607#if JOBS
3608 int stopstatus; /* status of a stopped job */
3609#endif
Denys Vlasenko4c179372017-01-11 18:44:15 +01003610 unsigned nprocs; /* number of processes */
3611
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003612#define JOBRUNNING 0 /* at least one proc running */
3613#define JOBSTOPPED 1 /* all procs are stopped */
3614#define JOBDONE 2 /* all procs are completed */
Denys Vlasenko4c179372017-01-11 18:44:15 +01003615 unsigned
3616 state: 8,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003617#if JOBS
3618 sigint: 1, /* job was killed by SIGINT */
3619 jobctl: 1, /* job running under job control */
3620#endif
3621 waited: 1, /* true if this entry has been waited for */
3622 used: 1, /* true if this entry is in used */
3623 changed: 1; /* true if status has changed */
3624 struct job *prev_job; /* previous job */
3625};
3626
Denis Vlasenko68404f12008-03-17 09:00:54 +00003627static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003628static int forkshell(struct job *, union node *, int);
3629static int waitforjob(struct job *);
3630
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003631#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003632enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003633#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003635static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003636static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003637#endif
3638
3639/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003640 * Ignore a signal.
3641 */
3642static void
3643ignoresig(int signo)
3644{
3645 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3646 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3647 /* No, need to do it */
3648 signal(signo, SIG_IGN);
3649 }
3650 sigmode[signo - 1] = S_HARD_IGN;
3651}
3652
3653/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003654 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003655 */
3656static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003657signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003658{
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003659 if (signo == SIGCHLD) {
3660 got_sigchld = 1;
3661 if (!trap[SIGCHLD])
3662 return;
3663 }
3664
Denis Vlasenko4b875702009-03-19 13:30:04 +00003665 gotsig[signo - 1] = 1;
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003666 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003667
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003668 if (signo == SIGINT && !trap[SIGINT]) {
3669 if (!suppress_int) {
3670 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003671 raise_interrupt(); /* does not return */
3672 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003673 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003674 }
3675}
3676
3677/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003678 * Set the signal handler for the specified signal. The routine figures
3679 * out what it should be set to.
3680 */
3681static void
3682setsignal(int signo)
3683{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003684 char *t;
3685 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686 struct sigaction act;
3687
3688 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003689 new_act = S_DFL;
3690 if (t != NULL) { /* trap for this sig is set */
3691 new_act = S_CATCH;
3692 if (t[0] == '\0') /* trap is "": ignore this sig */
3693 new_act = S_IGN;
3694 }
3695
3696 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003697 switch (signo) {
3698 case SIGINT:
3699 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003700 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003701 break;
3702 case SIGQUIT:
3703#if DEBUG
3704 if (debug)
3705 break;
3706#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003707 /* man bash:
3708 * "In all cases, bash ignores SIGQUIT. Non-builtin
3709 * commands run by bash have signal handlers
3710 * set to the values inherited by the shell
3711 * from its parent". */
3712 new_act = S_IGN;
3713 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003714 case SIGTERM:
3715 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003716 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003717 break;
3718#if JOBS
3719 case SIGTSTP:
3720 case SIGTTOU:
3721 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003722 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003723 break;
3724#endif
3725 }
3726 }
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003727 /* if !rootshell, we reset SIGQUIT to DFL,
3728 * whereas we have to restore it to what shell got on entry.
3729 * This is handled by the fact that if signal was IGNored on entry,
3730 * then cur_act is S_HARD_IGN and we never change its sigaction
3731 * (see code below).
3732 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003733
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003734 if (signo == SIGCHLD)
3735 new_act = S_CATCH;
3736
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003737 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003738 cur_act = *t;
3739 if (cur_act == 0) {
3740 /* current setting is not yet known */
3741 if (sigaction(signo, NULL, &act)) {
3742 /* pretend it worked; maybe we should give a warning,
3743 * but other shells don't. We don't alter sigmode,
3744 * so we retry every time.
3745 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003746 return;
3747 }
3748 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003749 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003750 if (mflag
3751 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3752 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003753 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003754 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003755 }
Denys Vlasenko0f14f412017-08-06 20:06:19 +02003756 if (act.sa_handler == SIG_DFL && new_act == S_DFL) {
3757 /* installing SIG_DFL over SIG_DFL is a no-op */
3758 /* saves one sigaction call in each "sh -c SCRIPT" invocation */
3759 *t = S_DFL;
3760 return;
3761 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003762 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003763 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003764 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003765
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003766 *t = new_act;
3767
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003768 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003769 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003771 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003772 break;
3773 case S_IGN:
3774 act.sa_handler = SIG_IGN;
3775 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003776 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003777 /* flags and mask matter only if !DFL and !IGN, but we do it
3778 * for all cases for more deterministic behavior:
3779 */
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003780 act.sa_flags = 0; //TODO: why not SA_RESTART?
Ian Wienand89b3cba2011-04-16 20:05:14 +02003781 sigfillset(&act.sa_mask);
3782
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003783 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784}
3785
3786/* mode flags for set_curjob */
3787#define CUR_DELETE 2
3788#define CUR_RUNNING 1
3789#define CUR_STOPPED 0
3790
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791#if JOBS
3792/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003793static int initialpgrp; //references:2
3794static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795#endif
3796/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003797static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003798/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003799static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003801static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802
Denys Vlasenko098b7132017-01-11 19:59:03 +01003803#if 0
3804/* Bash has a feature: it restores termios after a successful wait for
3805 * a foreground job which had at least one stopped or sigkilled member.
3806 * The probable rationale is that SIGSTOP and SIGKILL can preclude task from
3807 * properly restoring tty state. Should we do this too?
3808 * A reproducer: ^Z an interactive python:
3809 *
3810 * # python
3811 * Python 2.7.12 (...)
3812 * >>> ^Z
3813 * { python leaves tty in -icanon -echo state. We do survive that... }
3814 * [1]+ Stopped python
3815 * { ...however, next program (python #2) does not survive it well: }
3816 * # python
3817 * Python 2.7.12 (...)
3818 * >>> Traceback (most recent call last):
3819 * { above, I typed "qwerty<CR>", but -echo state is still in effect }
3820 * File "<stdin>", line 1, in <module>
3821 * NameError: name 'qwerty' is not defined
3822 *
3823 * The implementation below is modeled on bash code and seems to work.
3824 * However, I'm not sure we should do this. For one: what if I'd fg
3825 * the stopped python instead? It'll be confused by "restored" tty state.
3826 */
3827static struct termios shell_tty_info;
3828static void
3829get_tty_state(void)
3830{
3831 if (rootshell && ttyfd >= 0)
3832 tcgetattr(ttyfd, &shell_tty_info);
3833}
3834static void
3835set_tty_state(void)
3836{
3837 /* if (rootshell) - caller ensures this */
3838 if (ttyfd >= 0)
3839 tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
3840}
3841static int
3842job_signal_status(struct job *jp)
3843{
3844 int status;
3845 unsigned i;
3846 struct procstat *ps = jp->ps;
3847 for (i = 0; i < jp->nprocs; i++) {
3848 status = ps[i].ps_status;
3849 if (WIFSIGNALED(status) || WIFSTOPPED(status))
3850 return status;
3851 }
3852 return 0;
3853}
3854static void
3855restore_tty_if_stopped_or_signaled(struct job *jp)
3856{
3857//TODO: check what happens if we come from waitforjob() in expbackq()
3858 if (rootshell) {
3859 int s = job_signal_status(jp);
3860 if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
3861 set_tty_state();
3862 }
3863}
3864#else
3865# define get_tty_state() ((void)0)
3866# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
3867#endif
3868
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003869static void
3870set_curjob(struct job *jp, unsigned mode)
3871{
3872 struct job *jp1;
3873 struct job **jpp, **curp;
3874
3875 /* first remove from list */
3876 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003877 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003878 jp1 = *jpp;
3879 if (jp1 == jp)
3880 break;
3881 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003882 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883 *jpp = jp1->prev_job;
3884
3885 /* Then re-insert in correct position */
3886 jpp = curp;
3887 switch (mode) {
3888 default:
3889#if DEBUG
3890 abort();
3891#endif
3892 case CUR_DELETE:
3893 /* job being deleted */
3894 break;
3895 case CUR_RUNNING:
3896 /* newly created job or backgrounded job,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +01003897 * put after all stopped jobs.
3898 */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003899 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003900 jp1 = *jpp;
3901#if JOBS
3902 if (!jp1 || jp1->state != JOBSTOPPED)
3903#endif
3904 break;
3905 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003906 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003907 /* FALLTHROUGH */
3908#if JOBS
3909 case CUR_STOPPED:
3910#endif
3911 /* newly stopped job - becomes curjob */
3912 jp->prev_job = *jpp;
3913 *jpp = jp;
3914 break;
3915 }
3916}
3917
3918#if JOBS || DEBUG
3919static int
3920jobno(const struct job *jp)
3921{
3922 return jp - jobtab + 1;
3923}
3924#endif
3925
3926/*
3927 * Convert a job name to a job structure.
3928 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003929#if !JOBS
3930#define getjob(name, getctl) getjob(name)
3931#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932static struct job *
3933getjob(const char *name, int getctl)
3934{
3935 struct job *jp;
3936 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003937 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003938 unsigned num;
3939 int c;
3940 const char *p;
3941 char *(*match)(const char *, const char *);
3942
3943 jp = curjob;
3944 p = name;
3945 if (!p)
3946 goto currentjob;
3947
3948 if (*p != '%')
3949 goto err;
3950
3951 c = *++p;
3952 if (!c)
3953 goto currentjob;
3954
3955 if (!p[1]) {
3956 if (c == '+' || c == '%') {
3957 currentjob:
3958 err_msg = "No current job";
3959 goto check;
3960 }
3961 if (c == '-') {
3962 if (jp)
3963 jp = jp->prev_job;
3964 err_msg = "No previous job";
3965 check:
3966 if (!jp)
3967 goto err;
3968 goto gotit;
3969 }
3970 }
3971
3972 if (is_number(p)) {
3973 num = atoi(p);
Denys Vlasenko46a45ce2016-09-29 01:10:08 +02003974 if (num > 0 && num <= njobs) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003975 jp = jobtab + num - 1;
3976 if (jp->used)
3977 goto gotit;
3978 goto err;
3979 }
3980 }
3981
3982 match = prefix;
3983 if (*p == '?') {
3984 match = strstr;
3985 p++;
3986 }
3987
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003988 found = NULL;
3989 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003990 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003991 if (found)
3992 goto err;
3993 found = jp;
3994 err_msg = "%s: ambiguous";
3995 }
3996 jp = jp->prev_job;
3997 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003998 if (!found)
3999 goto err;
4000 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004001
4002 gotit:
4003#if JOBS
4004 err_msg = "job %s not created under job control";
4005 if (getctl && jp->jobctl == 0)
4006 goto err;
4007#endif
4008 return jp;
4009 err:
4010 ash_msg_and_raise_error(err_msg, name);
4011}
4012
4013/*
4014 * Mark a job structure as unused.
4015 */
4016static void
4017freejob(struct job *jp)
4018{
4019 struct procstat *ps;
4020 int i;
4021
4022 INT_OFF;
4023 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004024 if (ps->ps_cmd != nullstr)
4025 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004026 }
4027 if (jp->ps != &jp->ps0)
4028 free(jp->ps);
4029 jp->used = 0;
4030 set_curjob(jp, CUR_DELETE);
4031 INT_ON;
4032}
4033
4034#if JOBS
4035static void
4036xtcsetpgrp(int fd, pid_t pgrp)
4037{
4038 if (tcsetpgrp(fd, pgrp))
Ron Yorstonbe366e52017-07-27 13:53:39 +01004039 ash_msg_and_raise_perror("can't set tty process group");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004040}
4041
4042/*
4043 * Turn job control on and off.
4044 *
4045 * Note: This code assumes that the third arg to ioctl is a character
4046 * pointer, which is true on Berkeley systems but not System V. Since
4047 * System V doesn't have job control yet, this isn't a problem now.
4048 *
4049 * Called with interrupts off.
4050 */
4051static void
4052setjobctl(int on)
4053{
4054 int fd;
4055 int pgrp;
4056
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004057 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004058 return;
4059 if (on) {
4060 int ofd;
4061 ofd = fd = open(_PATH_TTY, O_RDWR);
4062 if (fd < 0) {
4063 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
4064 * That sometimes helps to acquire controlling tty.
4065 * Obviously, a workaround for bugs when someone
4066 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004067 fd = 2;
4068 while (!isatty(fd))
4069 if (--fd < 0)
4070 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004071 }
Denys Vlasenko64774602016-10-26 15:24:30 +02004072 /* fd is a tty at this point */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004073 fd = fcntl(fd, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko10ad6222017-04-17 16:13:32 +02004074 if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, don't */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004075 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004076 if (fd < 0)
Denys Vlasenko64774602016-10-26 15:24:30 +02004077 goto out; /* F_DUPFD failed */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004078 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
4079 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004080 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 pgrp = tcgetpgrp(fd);
4082 if (pgrp < 0) {
4083 out:
4084 ash_msg("can't access tty; job control turned off");
4085 mflag = on = 0;
4086 goto close;
4087 }
4088 if (pgrp == getpgrp())
4089 break;
4090 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004091 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004092 initialpgrp = pgrp;
4093
4094 setsignal(SIGTSTP);
4095 setsignal(SIGTTOU);
4096 setsignal(SIGTTIN);
4097 pgrp = rootpid;
4098 setpgid(0, pgrp);
4099 xtcsetpgrp(fd, pgrp);
4100 } else {
4101 /* turning job control off */
4102 fd = ttyfd;
4103 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004104 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004105 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004106 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004107 setpgid(0, pgrp);
4108 setsignal(SIGTSTP);
4109 setsignal(SIGTTOU);
4110 setsignal(SIGTTIN);
4111 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004112 if (fd >= 0)
4113 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004114 fd = -1;
4115 }
4116 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004117 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004118}
4119
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004120static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004121killcmd(int argc, char **argv)
4122{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004123 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004124 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004125 do {
4126 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004127 /*
4128 * "kill %N" - job kill
4129 * Converting to pgrp / pid kill
4130 */
4131 struct job *jp;
4132 char *dst;
4133 int j, n;
4134
4135 jp = getjob(argv[i], 0);
4136 /*
4137 * In jobs started under job control, we signal
4138 * entire process group by kill -PGRP_ID.
4139 * This happens, f.e., in interactive shell.
4140 *
4141 * Otherwise, we signal each child via
4142 * kill PID1 PID2 PID3.
4143 * Testcases:
4144 * sh -c 'sleep 1|sleep 1 & kill %1'
4145 * sh -c 'true|sleep 2 & sleep 1; kill %1'
4146 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4147 */
4148 n = jp->nprocs; /* can't be 0 (I hope) */
4149 if (jp->jobctl)
4150 n = 1;
4151 dst = alloca(n * sizeof(int)*4);
4152 argv[i] = dst;
4153 for (j = 0; j < n; j++) {
4154 struct procstat *ps = &jp->ps[j];
4155 /* Skip non-running and not-stopped members
4156 * (i.e. dead members) of the job
4157 */
4158 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
4159 continue;
4160 /*
4161 * kill_main has matching code to expect
4162 * leading space. Needed to not confuse
4163 * negative pids with "kill -SIGNAL_NO" syntax
4164 */
4165 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4166 }
4167 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004168 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004169 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004170 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004171 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004172}
4173
4174static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01004175showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176{
Denys Vlasenko285ad152009-12-04 23:02:27 +01004177 struct procstat *ps;
4178 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179
Denys Vlasenko285ad152009-12-04 23:02:27 +01004180 psend = jp->ps + jp->nprocs;
4181 for (ps = jp->ps + 1; ps < psend; ps++)
4182 printf(" | %s", ps->ps_cmd);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004183 newline_and_flush(stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184 flush_stdout_stderr();
4185}
4186
4187
4188static int
4189restartjob(struct job *jp, int mode)
4190{
4191 struct procstat *ps;
4192 int i;
4193 int status;
4194 pid_t pgid;
4195
4196 INT_OFF;
4197 if (jp->state == JOBDONE)
4198 goto out;
4199 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004200 pgid = jp->ps[0].ps_pid;
Denys Vlasenko098b7132017-01-11 19:59:03 +01004201 if (mode == FORK_FG) {
4202 get_tty_state();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004203 xtcsetpgrp(ttyfd, pgid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01004204 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004205 killpg(pgid, SIGCONT);
4206 ps = jp->ps;
4207 i = jp->nprocs;
4208 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004209 if (WIFSTOPPED(ps->ps_status)) {
4210 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004211 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004212 ps++;
4213 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214 out:
4215 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4216 INT_ON;
4217 return status;
4218}
4219
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004220static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004221fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004222{
4223 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004224 int mode;
4225 int retval;
4226
4227 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
4228 nextopt(nullstr);
4229 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004230 do {
4231 jp = getjob(*argv, 1);
4232 if (mode == FORK_BG) {
4233 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004234 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004235 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004236 out1str(jp->ps[0].ps_cmd);
4237 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004238 retval = restartjob(jp, mode);
4239 } while (*argv && *++argv);
4240 return retval;
4241}
4242#endif
4243
4244static int
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004245sprint_status48(char *os, int status, int sigonly)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004246{
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004247 char *s = os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004248 int st;
4249
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004250 if (!WIFEXITED(status)) {
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004251#if JOBS
4252 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004253 st = WSTOPSIG(status);
4254 else
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004255#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004256 st = WTERMSIG(status);
4257 if (sigonly) {
4258 if (st == SIGINT || st == SIGPIPE)
4259 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004260#if JOBS
4261 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004262 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004263#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264 }
4265 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01004266//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004267 //s = stpncpy(s, strsignal(st), 32); //not all libc have stpncpy()
4268 s += fmtstr(s, 32, strsignal(st));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004269 if (WCOREDUMP(status)) {
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004270 s = stpcpy(s, " (core dumped)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004271 }
4272 } else if (!sigonly) {
4273 st = WEXITSTATUS(status);
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004274 s += fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004275 }
4276 out:
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004277 return s - os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004278}
4279
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004280#define DOWAIT_NONBLOCK 0
4281#define DOWAIT_BLOCK 1
4282#define DOWAIT_BLOCK_OR_SIG 2
Ron Yorstone48559e2019-03-31 09:27:09 +01004283#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004284# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4285#endif
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004286
4287static int
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004288waitproc(int block, int *status)
4289{
4290 sigset_t oldmask;
4291 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4292 int err;
4293
4294#if JOBS
4295 if (doing_jobctl)
4296 flags |= WUNTRACED;
4297#endif
4298
4299 do {
4300 got_sigchld = 0;
4301 do
4302 err = waitpid(-1, status, flags);
4303 while (err < 0 && errno == EINTR);
4304
4305 if (err || (err = -!block))
4306 break;
4307
4308 sigfillset(&oldmask);
4309 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4310 while (!got_sigchld && !pending_sig)
4311 sigsuspend(&oldmask);
4312 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4313 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4314 //while (!got_sigchld && !pending_sig)
4315 // pause();
4316
4317 } while (got_sigchld);
4318
4319 return err;
4320}
4321
4322static int
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004323waitone(int block, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004324{
4325 int pid;
4326 int status;
4327 struct job *jp;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004328 struct job *thisjob = NULL;
Ron Yorstone48559e2019-03-31 09:27:09 +01004329#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004330 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
4331 block = (block & ~DOWAIT_JOBSTATUS);
4332#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004333
Denys Vlasenkob543bda2016-10-27 20:08:28 +02004334 TRACE(("dowait(0x%x) called\n", block));
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00004335
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004336 /* It's wrong to call waitpid() outside of INT_OFF region:
4337 * signal can arrive just after syscall return and handler can
4338 * longjmp away, losing stop/exit notification processing.
4339 * Thus, for "jobs" builtin, and for waiting for a fg job,
4340 * we call waitpid() (blocking or non-blocking) inside INT_OFF.
4341 *
4342 * However, for "wait" builtin it is wrong to simply call waitpid()
4343 * in INT_OFF region: "wait" needs to wait for any running job
4344 * to change state, but should exit on any trap too.
4345 * In INT_OFF region, a signal just before syscall entry can set
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004346 * pending_sig variables, but we can't check them, and we would
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004347 * either enter a sleeping waitpid() (BUG), or need to busy-loop.
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004348 *
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004349 * Because of this, we run inside INT_OFF, but use a special routine
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004350 * which combines waitpid() and sigsuspend().
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004351 * This is the reason why we need to have a handler for SIGCHLD:
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004352 * SIG_DFL handler does not wake sigsuspend().
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004353 */
4354 INT_OFF;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004355 pid = waitproc(block, &status);
4356 TRACE(("wait returns pid %d, status=%d\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004357 if (pid <= 0)
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004358 goto out;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004359
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004360 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004361 int jobstate;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004362 struct procstat *ps;
4363 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004364 if (jp->state == JOBDONE)
4365 continue;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004366 jobstate = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004367 ps = jp->ps;
4368 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004369 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004370 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004371 TRACE(("Job %d: changing status of proc %d "
4372 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01004373 jobno(jp), pid, ps->ps_status, status));
4374 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004375 thisjob = jp;
4376 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004377 if (ps->ps_status == -1)
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004378 jobstate = JOBRUNNING;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004379#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004380 if (jobstate == JOBRUNNING)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004381 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004382 if (WIFSTOPPED(ps->ps_status)) {
4383 jp->stopstatus = ps->ps_status;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004384 jobstate = JOBSTOPPED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004385 }
4386#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01004387 } while (++ps < psend);
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004388 if (!thisjob)
4389 continue;
4390
4391 /* Found the job where one of its processes changed its state.
4392 * Is there at least one live and running process in this job? */
4393 if (jobstate != JOBRUNNING) {
4394 /* No. All live processes in the job are stopped
4395 * (JOBSTOPPED) or there are no live processes (JOBDONE)
4396 */
4397 thisjob->changed = 1;
4398 if (thisjob->state != jobstate) {
4399 TRACE(("Job %d: changing state from %d to %d\n",
4400 jobno(thisjob), thisjob->state, jobstate));
4401 thisjob->state = jobstate;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004402#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004403 if (jobstate == JOBSTOPPED)
4404 set_curjob(thisjob, CUR_STOPPED);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004405#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004406 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004407 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004408 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004409 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004410 /* The process wasn't found in job list */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004411 out:
4412 INT_ON;
4413
Ron Yorstone48559e2019-03-31 09:27:09 +01004414#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004415 if (want_jobexitstatus) {
4416 pid = -1;
4417 if (thisjob && thisjob->state == JOBDONE)
4418 pid = thisjob->ps[thisjob->nprocs - 1].ps_status;
4419 }
4420#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004421 if (thisjob && thisjob == job) {
4422 char s[48 + 1];
4423 int len;
4424
Denys Vlasenko9c541002015-10-07 15:44:36 +02004425 len = sprint_status48(s, status, 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004426 if (len) {
4427 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004428 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004429 out2str(s);
4430 }
4431 }
4432 return pid;
4433}
4434
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004435static int
4436dowait(int block, struct job *jp)
4437{
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004438 smallint gotchld = *(volatile smallint *)&got_sigchld;
4439 int rpid;
4440 int pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004441
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004442 if (jp && jp->state != JOBRUNNING)
4443 block = DOWAIT_NONBLOCK;
4444
4445 if (block == DOWAIT_NONBLOCK && !gotchld)
4446 return 1;
4447
4448 rpid = 1;
4449
4450 do {
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004451 pid = waitone(block, jp);
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004452 rpid &= !!pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004453
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004454 if (!pid || (jp && jp->state != JOBRUNNING))
4455 block = DOWAIT_NONBLOCK;
4456 } while (pid >= 0);
4457
4458 return rpid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004459}
4460
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004461#if JOBS
4462static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004463showjob(struct job *jp, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004464{
4465 struct procstat *ps;
4466 struct procstat *psend;
4467 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004468 int indent_col;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004469 char s[16 + 16 + 48];
4470 FILE *out = (mode & SHOW_STDERR ? stderr : stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004471
4472 ps = jp->ps;
4473
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004474 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004475 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004476 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004477 return;
4478 }
4479
4480 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004481 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004482
4483 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004484 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004486 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004487
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004488 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004489 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004490
4491 psend = ps + jp->nprocs;
4492
4493 if (jp->state == JOBRUNNING) {
4494 strcpy(s + col, "Running");
4495 col += sizeof("Running") - 1;
4496 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004497 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004498 if (jp->state == JOBSTOPPED)
4499 status = jp->stopstatus;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004500 col += sprint_status48(s + col, status, 0);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004501 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004502 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004503
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004504 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4505 * or prints several "PID | <cmdN>" lines,
4506 * depending on SHOW_PIDS bit.
4507 * We do not print status of individual processes
4508 * between PID and <cmdN>. bash does it, but not very well:
4509 * first line shows overall job status, not process status,
4510 * making it impossible to know 1st process status.
4511 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004512 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004513 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004514 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004515 s[0] = '\0';
4516 col = 33;
4517 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004518 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004519 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004520 fprintf(out, "%s%*c%s%s",
4521 s,
4522 33 - col >= 0 ? 33 - col : 0, ' ',
4523 ps == jp->ps ? "" : "| ",
4524 ps->ps_cmd
4525 );
4526 } while (++ps != psend);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004527 newline_and_flush(out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004528
4529 jp->changed = 0;
4530
4531 if (jp->state == JOBDONE) {
4532 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4533 freejob(jp);
4534 }
4535}
4536
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537/*
4538 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4539 * statuses have changed since the last call to showjobs.
4540 */
4541static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004542showjobs(int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004543{
4544 struct job *jp;
4545
Denys Vlasenko883cea42009-07-11 15:31:59 +02004546 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004547
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004548 /* Handle all finished jobs */
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004549 dowait(DOWAIT_NONBLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004550
4551 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004552 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004553 showjob(jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004554 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004555 }
4556}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004557
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004558static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004559jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004560{
4561 int mode, m;
4562
4563 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004564 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004565 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004566 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004567 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004568 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004569 }
4570
4571 argv = argptr;
4572 if (*argv) {
4573 do
Denys Vlasenko9c541002015-10-07 15:44:36 +02004574 showjob(getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004575 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004576 } else {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004577 showjobs(mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004578 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004579
4580 return 0;
4581}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004582#endif /* JOBS */
4583
Michael Abbott359da5e2009-12-04 23:03:29 +01004584/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004585static int
4586getstatus(struct job *job)
4587{
4588 int status;
4589 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004590 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004591
Michael Abbott359da5e2009-12-04 23:03:29 +01004592 /* Fetch last member's status */
4593 ps = job->ps + job->nprocs - 1;
4594 status = ps->ps_status;
4595 if (pipefail) {
4596 /* "set -o pipefail" mode: use last _nonzero_ status */
4597 while (status == 0 && --ps >= job->ps)
4598 status = ps->ps_status;
4599 }
4600
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004601 retval = WEXITSTATUS(status);
4602 if (!WIFEXITED(status)) {
4603#if JOBS
4604 retval = WSTOPSIG(status);
4605 if (!WIFSTOPPED(status))
4606#endif
4607 {
4608 /* XXX: limits number of signals */
4609 retval = WTERMSIG(status);
4610#if JOBS
4611 if (retval == SIGINT)
4612 job->sigint = 1;
4613#endif
4614 }
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004615 retval |= 128;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004616 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004617 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004618 jobno(job), job->nprocs, status, retval));
4619 return retval;
4620}
4621
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004622static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004623waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004624{
4625 struct job *job;
4626 int retval;
4627 struct job *jp;
Ron Yorstone48559e2019-03-31 09:27:09 +01004628#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004629 int status;
4630 char one = nextopt("n");
4631#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004632 nextopt(nullstr);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004633#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004634 retval = 0;
4635
4636 argv = argptr;
Denys Vlasenko966f0872019-03-27 15:51:42 +01004637 if (!argv[0]) {
4638 /* wait for all jobs / one job if -n */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004639 for (;;) {
4640 jp = curjob;
Ron Yorstone48559e2019-03-31 09:27:09 +01004641#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004642 if (one && !jp)
4643 /* exitcode of "wait -n" with nothing to wait for is 127, not 0 */
4644 retval = 127;
4645#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004646 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004647 if (!jp) /* no running procs */
4648 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004649 if (jp->state == JOBRUNNING)
4650 break;
4651 jp->waited = 1;
4652 jp = jp->prev_job;
4653 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004654 /* man bash:
4655 * "When bash is waiting for an asynchronous command via
4656 * the wait builtin, the reception of a signal for which a trap
4657 * has been set will cause the wait builtin to return immediately
4658 * with an exit status greater than 128, immediately after which
4659 * the trap is executed."
Denys Vlasenko69188112016-10-27 20:18:18 +02004660 */
Ron Yorstone48559e2019-03-31 09:27:09 +01004661#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004662 status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL);
4663#else
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004664 dowait(DOWAIT_BLOCK_OR_SIG, NULL);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004665#endif
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004666 /* if child sends us a signal *and immediately exits*,
4667 * dowait() returns pid > 0. Check this case,
4668 * not "if (dowait() < 0)"!
4669 */
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004670 if (pending_sig)
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004671 goto sigout;
Ron Yorstone48559e2019-03-31 09:27:09 +01004672#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004673 if (one) {
4674 /* wait -n waits for one _job_, not one _process_.
4675 * date; sleep 3 & sleep 2 | sleep 1 & wait -n; date
4676 * should wait for 2 seconds. Not 1 or 3.
4677 */
4678 if (status != -1 && !WIFSTOPPED(status)) {
4679 retval = WEXITSTATUS(status);
4680 if (WIFSIGNALED(status))
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004681 retval = 128 | WTERMSIG(status);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004682 goto ret;
4683 }
4684 }
4685#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004686 }
4687 }
4688
4689 retval = 127;
4690 do {
4691 if (**argv != '%') {
4692 pid_t pid = number(*argv);
4693 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004694 while (1) {
4695 if (!job)
4696 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004697 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004698 break;
4699 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004700 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004701 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004702 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004703 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004704 /* loop until process terminated or stopped */
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004705 dowait(DOWAIT_BLOCK_OR_SIG, job);
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004706 if (pending_sig)
4707 goto sigout;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004708 job->waited = 1;
4709 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004710 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004711 } while (*++argv);
4712
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004713 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004714 return retval;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004715 sigout:
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004716 retval = 128 | pending_sig;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004717 return retval;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004718}
4719
4720static struct job *
4721growjobtab(void)
4722{
4723 size_t len;
4724 ptrdiff_t offset;
4725 struct job *jp, *jq;
4726
4727 len = njobs * sizeof(*jp);
4728 jq = jobtab;
4729 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4730
4731 offset = (char *)jp - (char *)jq;
4732 if (offset) {
4733 /* Relocate pointers */
4734 size_t l = len;
4735
4736 jq = (struct job *)((char *)jq + l);
4737 while (l) {
4738 l -= sizeof(*jp);
4739 jq--;
4740#define joff(p) ((struct job *)((char *)(p) + l))
4741#define jmove(p) (p) = (void *)((char *)(p) + offset)
4742 if (joff(jp)->ps == &jq->ps0)
4743 jmove(joff(jp)->ps);
4744 if (joff(jp)->prev_job)
4745 jmove(joff(jp)->prev_job);
4746 }
4747 if (curjob)
4748 jmove(curjob);
4749#undef joff
4750#undef jmove
4751 }
4752
4753 njobs += 4;
4754 jobtab = jp;
4755 jp = (struct job *)((char *)jp + len);
4756 jq = jp + 3;
4757 do {
4758 jq->used = 0;
4759 } while (--jq >= jp);
4760 return jp;
4761}
4762
4763/*
4764 * Return a new job structure.
4765 * Called with interrupts off.
4766 */
4767static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004768makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004769{
4770 int i;
4771 struct job *jp;
4772
4773 for (i = njobs, jp = jobtab; ; jp++) {
4774 if (--i < 0) {
4775 jp = growjobtab();
4776 break;
4777 }
4778 if (jp->used == 0)
4779 break;
4780 if (jp->state != JOBDONE || !jp->waited)
4781 continue;
4782#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004783 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004784 continue;
4785#endif
4786 freejob(jp);
4787 break;
4788 }
4789 memset(jp, 0, sizeof(*jp));
4790#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004791 /* jp->jobctl is a bitfield.
Denys Vlasenko098b7132017-01-11 19:59:03 +01004792 * "jp->jobctl |= doing_jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004793 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004794 jp->jobctl = 1;
4795#endif
4796 jp->prev_job = curjob;
4797 curjob = jp;
4798 jp->used = 1;
4799 jp->ps = &jp->ps0;
4800 if (nprocs > 1) {
4801 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4802 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004803 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004804 jobno(jp)));
4805 return jp;
4806}
4807
4808#if JOBS
4809/*
4810 * Return a string identifying a command (to be printed by the
4811 * jobs command).
4812 */
4813static char *cmdnextc;
4814
4815static void
4816cmdputs(const char *s)
4817{
Denys Vlasenko965b7952020-11-30 13:03:03 +01004818 static const char vstype[VSTYPE + 1][3] ALIGN1 = {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004819 "", "}", "-", "+", "?", "=",
4820 "%", "%%", "#", "##"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01004821 IF_BASH_SUBSTR(, ":")
4822 IF_BASH_PATTERN_SUBST(, "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004823 };
4824
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004825 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004826 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004827 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004828 unsigned char c;
4829 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004830 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004831
Denys Vlasenko46a14772009-12-10 21:27:13 +01004832 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004833 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4834 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004835 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004836 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004837 switch (c) {
4838 case CTLESC:
4839 c = *p++;
4840 break;
4841 case CTLVAR:
4842 subtype = *p++;
4843 if ((subtype & VSTYPE) == VSLENGTH)
4844 str = "${#";
4845 else
4846 str = "${";
Ron Yorston549deab2015-05-18 09:57:51 +02004847 goto dostr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004848 case CTLENDVAR:
Denys Vlasenko3f4847b2020-02-16 19:06:42 +01004849 str = "\"}";
4850 str += !(quoted & 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004851 quoted >>= 1;
4852 subtype = 0;
4853 goto dostr;
4854 case CTLBACKQ:
4855 str = "$(...)";
4856 goto dostr;
Denys Vlasenko0b883582016-12-23 16:49:07 +01004857#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004858 case CTLARI:
4859 str = "$((";
4860 goto dostr;
4861 case CTLENDARI:
4862 str = "))";
4863 goto dostr;
4864#endif
4865 case CTLQUOTEMARK:
4866 quoted ^= 1;
4867 c = '"';
4868 break;
4869 case '=':
4870 if (subtype == 0)
4871 break;
4872 if ((subtype & VSTYPE) != VSNORMAL)
4873 quoted <<= 1;
4874 str = vstype[subtype & VSTYPE];
4875 if (subtype & VSNUL)
4876 c = ':';
4877 else
4878 goto checkstr;
4879 break;
4880 case '\'':
4881 case '\\':
4882 case '"':
4883 case '$':
4884 /* These can only happen inside quotes */
4885 cc[0] = c;
4886 str = cc;
Denys Vlasenkod0fff912017-07-31 14:32:18 +02004887//FIXME:
4888// $ true $$ &
4889// $ <cr>
4890// [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004891 c = '\\';
4892 break;
4893 default:
4894 break;
4895 }
4896 USTPUTC(c, nextc);
4897 checkstr:
4898 if (!str)
4899 continue;
4900 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004901 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004902 USTPUTC(c, nextc);
4903 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004904 } /* while *p++ not NUL */
4905
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004906 if (quoted & 1) {
4907 USTPUTC('"', nextc);
4908 }
4909 *nextc = 0;
4910 cmdnextc = nextc;
4911}
4912
4913/* cmdtxt() and cmdlist() call each other */
4914static void cmdtxt(union node *n);
4915
4916static void
4917cmdlist(union node *np, int sep)
4918{
4919 for (; np; np = np->narg.next) {
4920 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004921 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004922 cmdtxt(np);
4923 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004924 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004925 }
4926}
4927
4928static void
4929cmdtxt(union node *n)
4930{
4931 union node *np;
4932 struct nodelist *lp;
4933 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004934
4935 if (!n)
4936 return;
4937 switch (n->type) {
4938 default:
4939#if DEBUG
4940 abort();
4941#endif
4942 case NPIPE:
4943 lp = n->npipe.cmdlist;
4944 for (;;) {
4945 cmdtxt(lp->n);
4946 lp = lp->next;
4947 if (!lp)
4948 break;
4949 cmdputs(" | ");
4950 }
4951 break;
4952 case NSEMI:
4953 p = "; ";
4954 goto binop;
4955 case NAND:
4956 p = " && ";
4957 goto binop;
4958 case NOR:
4959 p = " || ";
4960 binop:
4961 cmdtxt(n->nbinary.ch1);
4962 cmdputs(p);
4963 n = n->nbinary.ch2;
4964 goto donode;
4965 case NREDIR:
4966 case NBACKGND:
4967 n = n->nredir.n;
4968 goto donode;
4969 case NNOT:
4970 cmdputs("!");
4971 n = n->nnot.com;
4972 donode:
4973 cmdtxt(n);
4974 break;
4975 case NIF:
4976 cmdputs("if ");
4977 cmdtxt(n->nif.test);
4978 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004979 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004980 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004981 cmdputs("; else ");
4982 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004983 } else {
4984 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004985 }
4986 p = "; fi";
4987 goto dotail;
4988 case NSUBSHELL:
4989 cmdputs("(");
4990 n = n->nredir.n;
4991 p = ")";
4992 goto dotail;
4993 case NWHILE:
4994 p = "while ";
4995 goto until;
4996 case NUNTIL:
4997 p = "until ";
4998 until:
4999 cmdputs(p);
5000 cmdtxt(n->nbinary.ch1);
5001 n = n->nbinary.ch2;
5002 p = "; done";
5003 dodo:
5004 cmdputs("; do ");
5005 dotail:
5006 cmdtxt(n);
5007 goto dotail2;
5008 case NFOR:
5009 cmdputs("for ");
5010 cmdputs(n->nfor.var);
5011 cmdputs(" in ");
5012 cmdlist(n->nfor.args, 1);
5013 n = n->nfor.body;
5014 p = "; done";
5015 goto dodo;
5016 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01005017 cmdputs(n->ndefun.text);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005018 p = "() { ... }";
5019 goto dotail2;
5020 case NCMD:
5021 cmdlist(n->ncmd.args, 1);
5022 cmdlist(n->ncmd.redirect, 0);
5023 break;
5024 case NARG:
5025 p = n->narg.text;
5026 dotail2:
5027 cmdputs(p);
5028 break;
5029 case NHERE:
5030 case NXHERE:
5031 p = "<<...";
5032 goto dotail2;
5033 case NCASE:
5034 cmdputs("case ");
5035 cmdputs(n->ncase.expr->narg.text);
5036 cmdputs(" in ");
5037 for (np = n->ncase.cases; np; np = np->nclist.next) {
5038 cmdtxt(np->nclist.pattern);
5039 cmdputs(") ");
5040 cmdtxt(np->nclist.body);
5041 cmdputs(";; ");
5042 }
5043 p = "esac";
5044 goto dotail2;
5045 case NTO:
5046 p = ">";
5047 goto redir;
5048 case NCLOBBER:
5049 p = ">|";
5050 goto redir;
5051 case NAPPEND:
5052 p = ">>";
5053 goto redir;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005054#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005055 case NTO2:
5056#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005057 case NTOFD:
5058 p = ">&";
5059 goto redir;
5060 case NFROM:
5061 p = "<";
5062 goto redir;
5063 case NFROMFD:
5064 p = "<&";
5065 goto redir;
5066 case NFROMTO:
5067 p = "<>";
5068 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005069 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005070 cmdputs(p);
5071 if (n->type == NTOFD || n->type == NFROMFD) {
Denys Vlasenkod0fff912017-07-31 14:32:18 +02005072 if (n->ndup.dupfd >= 0)
5073 cmdputs(utoa(n->ndup.dupfd));
5074 else
5075 cmdputs("-");
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005076 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005077 }
5078 n = n->nfile.fname;
5079 goto donode;
5080 }
5081}
5082
5083static char *
5084commandtext(union node *n)
5085{
5086 char *name;
5087
5088 STARTSTACKSTR(cmdnextc);
5089 cmdtxt(n);
5090 name = stackblock();
Denys Vlasenko6a94cee2016-10-25 17:40:25 +02005091 TRACE(("commandtext: name %p, end %p\n", name, cmdnextc));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005092 return ckstrdup(name);
5093}
5094#endif /* JOBS */
5095
5096/*
5097 * Fork off a subshell. If we are doing job control, give the subshell its
5098 * own process group. Jp is a job structure that the job is to be added to.
5099 * N is the command that will be evaluated by the child. Both jp and n may
5100 * be NULL. The mode parameter can be one of the following:
5101 * FORK_FG - Fork off a foreground process.
5102 * FORK_BG - Fork off a background process.
5103 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
5104 * process group even if job control is on.
5105 *
5106 * When job control is turned off, background processes have their standard
5107 * input redirected to /dev/null (except for the second and later processes
5108 * in a pipeline).
5109 *
5110 * Called with interrupts off.
5111 */
5112/*
5113 * Clear traps on a fork.
5114 */
5115static void
5116clear_traps(void)
5117{
5118 char **tp;
5119
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005120 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005121 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005122 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005123 if (trap_ptr == trap)
5124 free(*tp);
5125 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005126 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02005127 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005128 setsignal(tp - trap);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005129 }
5130 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02005131 may_have_traps = 0;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005132 INT_ON;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005133}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005134
5135/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005136static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00005137
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005138/* Called after fork(), in child */
Denys Vlasenko70392332016-10-27 02:31:55 +02005139/* jp and n are NULL when called by openhere() for heredoc support */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02005140static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005141forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005142{
5143 int oldlvl;
5144
5145 TRACE(("Child shell %d\n", getpid()));
5146 oldlvl = shlvl;
5147 shlvl++;
5148
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005149 /* man bash: "Non-builtin commands run by bash have signal handlers
5150 * set to the values inherited by the shell from its parent".
5151 * Do we do it correctly? */
5152
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005153 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02005154
5155 if (mode == FORK_NOJOB /* is it `xxx` ? */
5156 && n && n->type == NCMD /* is it single cmd? */
5157 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01005158 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005159 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
5160 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
5161 ) {
5162 TRACE(("Trap hack\n"));
5163 /* Awful hack for `trap` or $(trap).
5164 *
5165 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5166 * contains an example where "trap" is executed in a subshell:
5167 *
5168 * save_traps=$(trap)
5169 * ...
5170 * eval "$save_traps"
5171 *
5172 * Standard does not say that "trap" in subshell shall print
5173 * parent shell's traps. It only says that its output
5174 * must have suitable form, but then, in the above example
5175 * (which is not supposed to be normative), it implies that.
5176 *
5177 * bash (and probably other shell) does implement it
5178 * (traps are reset to defaults, but "trap" still shows them),
5179 * but as a result, "trap" logic is hopelessly messed up:
5180 *
5181 * # trap
5182 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5183 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5184 * # true | trap <--- trap is in subshell - no output (ditto)
5185 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5186 * trap -- 'echo Ho' SIGWINCH
5187 * # echo `(trap)` <--- in subshell in subshell - output
5188 * trap -- 'echo Ho' SIGWINCH
5189 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5190 * trap -- 'echo Ho' SIGWINCH
5191 *
5192 * The rules when to forget and when to not forget traps
5193 * get really complex and nonsensical.
5194 *
5195 * Our solution: ONLY bare $(trap) or `trap` is special.
5196 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005197 /* Save trap handler strings for trap builtin to print */
Ron Yorstond840c5d2015-07-19 23:05:20 +02005198 trap_ptr = xmemdup(trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005199 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02005200 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005201 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005202#if JOBS
5203 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005204 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005205 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005206 pid_t pgrp;
5207
5208 if (jp->nprocs == 0)
5209 pgrp = getpid();
5210 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005211 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005212 /* this can fail because we are doing it in the parent also */
5213 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005214 if (mode == FORK_FG)
5215 xtcsetpgrp(ttyfd, pgrp);
5216 setsignal(SIGTSTP);
5217 setsignal(SIGTTOU);
5218 } else
5219#endif
5220 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005221 /* man bash: "When job control is not in effect,
5222 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005223 ignoresig(SIGINT);
5224 ignoresig(SIGQUIT);
5225 if (jp->nprocs == 0) {
5226 close(0);
5227 if (open(bb_dev_null, O_RDONLY) != 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005228 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005229 }
5230 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005231 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005232 if (iflag) { /* why if iflag only? */
5233 setsignal(SIGINT);
5234 setsignal(SIGTERM);
5235 }
5236 /* man bash:
5237 * "In all cases, bash ignores SIGQUIT. Non-builtin
5238 * commands run by bash have signal handlers
5239 * set to the values inherited by the shell
5240 * from its parent".
5241 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005242 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005243 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005244#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02005245 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01005246 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005247 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005248 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02005249 /* "jobs": we do not want to clear job list for it,
5250 * instead we remove only _its_ own_ job from job list.
5251 * This makes "jobs .... | cat" more useful.
5252 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005253 freejob(curjob);
5254 return;
5255 }
5256#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005257 for (jp = curjob; jp; jp = jp->prev_job)
5258 freejob(jp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005259}
5260
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005261/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00005262#if !JOBS
5263#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5264#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005265static void
5266forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5267{
5268 TRACE(("In parent shell: child = %d\n", pid));
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005269 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005270 return;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005271#if JOBS
5272 if (mode != FORK_NOJOB && jp->jobctl) {
5273 int pgrp;
5274
5275 if (jp->nprocs == 0)
5276 pgrp = pid;
5277 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005278 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005279 /* This can fail because we are doing it in the child also */
5280 setpgid(pid, pgrp);
5281 }
5282#endif
5283 if (mode == FORK_BG) {
5284 backgndpid = pid; /* set $! */
5285 set_curjob(jp, CUR_RUNNING);
5286 }
5287 if (jp) {
5288 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01005289 ps->ps_pid = pid;
5290 ps->ps_status = -1;
5291 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005292#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005293 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01005294 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005295#endif
5296 }
5297}
5298
Denys Vlasenko70392332016-10-27 02:31:55 +02005299/* jp and n are NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005300static int
5301forkshell(struct job *jp, union node *n, int mode)
5302{
5303 int pid;
5304
5305 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
5306 pid = fork();
5307 if (pid < 0) {
5308 TRACE(("Fork failed, errno=%d", errno));
5309 if (jp)
5310 freejob(jp);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005311 ash_msg_and_raise_perror("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005312 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02005313 if (pid == 0) {
5314 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005315 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005316 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005317 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005318 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005319 return pid;
5320}
5321
5322/*
5323 * Wait for job to finish.
5324 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005325 * Under job control we have the problem that while a child process
5326 * is running interrupts generated by the user are sent to the child
5327 * but not to the shell. This means that an infinite loop started by
5328 * an interactive user may be hard to kill. With job control turned off,
5329 * an interactive user may place an interactive program inside a loop.
5330 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005331 * these interrupts to also abort the loop. The approach we take here
5332 * is to have the shell ignore interrupt signals while waiting for a
5333 * foreground process to terminate, and then send itself an interrupt
5334 * signal if the child process was terminated by an interrupt signal.
5335 * Unfortunately, some programs want to do a bit of cleanup and then
5336 * exit on interrupt; unless these processes terminate themselves by
5337 * sending a signal to themselves (instead of calling exit) they will
5338 * confuse this approach.
5339 *
5340 * Called with interrupts off.
5341 */
5342static int
5343waitforjob(struct job *jp)
5344{
5345 int st;
5346
Denys Vlasenkod81af722020-02-18 14:28:30 +01005347 TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005348
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005349 /* In non-interactive shells, we _can_ get
5350 * a keyboard signal here and be EINTRed, but we just loop
5351 * inside dowait(), waiting for command to complete.
5352 *
5353 * man bash:
5354 * "If bash is waiting for a command to complete and receives
5355 * a signal for which a trap has been set, the trap
5356 * will not be executed until the command completes."
5357 *
5358 * Reality is that even if trap is not set, bash
5359 * will not act on the signal until command completes.
5360 * Try this. sleep5intoff.c:
5361 * #include <signal.h>
5362 * #include <unistd.h>
5363 * int main() {
5364 * sigset_t set;
5365 * sigemptyset(&set);
5366 * sigaddset(&set, SIGINT);
5367 * sigaddset(&set, SIGQUIT);
5368 * sigprocmask(SIG_BLOCK, &set, NULL);
5369 * sleep(5);
5370 * return 0;
5371 * }
5372 * $ bash -c './sleep5intoff; echo hi'
5373 * ^C^C^C^C <--- pressing ^C once a second
5374 * $ _
5375 * $ bash -c './sleep5intoff; echo hi'
5376 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
5377 * $ _
5378 */
5379 dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
5380 if (!jp)
Denys Vlasenko97edfc42020-02-18 14:37:56 +01005381 return exitstatus;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005382
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005383 st = getstatus(jp);
5384#if JOBS
5385 if (jp->jobctl) {
5386 xtcsetpgrp(ttyfd, rootpid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01005387 restore_tty_if_stopped_or_signaled(jp);
5388
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005389 /*
5390 * This is truly gross.
5391 * If we're doing job control, then we did a TIOCSPGRP which
5392 * caused us (the shell) to no longer be in the controlling
5393 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
5394 * intuit from the subprocess exit status whether a SIGINT
5395 * occurred, and if so interrupt ourselves. Yuck. - mycroft
5396 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005397 if (jp->sigint) /* TODO: do the same with all signals */
5398 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005399 }
5400 if (jp->state == JOBDONE)
5401#endif
5402 freejob(jp);
5403 return st;
5404}
5405
5406/*
5407 * return 1 if there are stopped jobs, otherwise 0
5408 */
5409static int
5410stoppedjobs(void)
5411{
5412 struct job *jp;
5413 int retval;
5414
5415 retval = 0;
5416 if (job_warning)
5417 goto out;
5418 jp = curjob;
5419 if (jp && jp->state == JOBSTOPPED) {
5420 out2str("You have stopped jobs.\n");
5421 job_warning = 2;
5422 retval++;
5423 }
5424 out:
5425 return retval;
5426}
5427
5428
Denys Vlasenko70392332016-10-27 02:31:55 +02005429/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005430 * Code for dealing with input/output redirection.
5431 */
5432
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01005433#undef EMPTY
5434#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005435#define EMPTY -2 /* marks an unused slot in redirtab */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005436#define CLOSED -1 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005437
5438/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005439 * Handle here documents. Normally we fork off a process to write the
5440 * data to a pipe. If the document is short, we can stuff the data in
5441 * the pipe without forking.
5442 */
5443/* openhere needs this forward reference */
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005444static void expandhere(union node *arg);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005445static int
5446openhere(union node *redir)
5447{
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005448 char *p;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005449 int pip[2];
5450 size_t len = 0;
5451
5452 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005453 ash_msg_and_raise_perror("can't create pipe");
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005454
5455 p = redir->nhere.doc->narg.text;
5456 if (redir->type == NXHERE) {
5457 expandhere(redir->nhere.doc);
5458 p = stackblock();
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005459 }
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005460
5461 len = strlen(p);
5462 if (len <= PIPE_BUF) {
5463 xwrite(pip[1], p, len);
5464 goto out;
5465 }
5466
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005467 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005468 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005469 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005470 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5471 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5472 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5473 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005474 signal(SIGPIPE, SIG_DFL);
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005475 xwrite(pip[1], p, len);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005476 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005477 }
5478 out:
5479 close(pip[1]);
5480 return pip[0];
5481}
5482
5483static int
5484openredirect(union node *redir)
5485{
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005486 struct stat sb;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005487 char *fname;
5488 int f;
5489
5490 switch (redir->nfile.type) {
Denys Vlasenko557482c2016-09-25 21:24:04 +02005491/* Can't happen, our single caller does this itself */
5492// case NTOFD:
5493// case NFROMFD:
5494// return -1;
5495 case NHERE:
5496 case NXHERE:
5497 return openhere(redir);
5498 }
5499
5500 /* For N[X]HERE, reading redir->nfile.expfname would touch beyond
5501 * allocated space. Do it only when we know it is safe.
5502 */
5503 fname = redir->nfile.expfname;
5504
5505 switch (redir->nfile.type) {
5506 default:
5507#if DEBUG
5508 abort();
5509#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005510 case NFROM:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005511 f = open(fname, O_RDONLY);
5512 if (f < 0)
5513 goto eopen;
5514 break;
5515 case NFROMTO:
Andreas Bühmannda75f442010-06-24 04:32:37 +02005516 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005517 if (f < 0)
5518 goto ecreate;
5519 break;
5520 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005521#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005522 case NTO2:
5523#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005524 /* Take care of noclobber mode. */
5525 if (Cflag) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005526 if (stat(fname, &sb) < 0) {
5527 f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5528 if (f < 0)
5529 goto ecreate;
5530 } else if (!S_ISREG(sb.st_mode)) {
5531 f = open(fname, O_WRONLY, 0666);
5532 if (f < 0)
5533 goto ecreate;
Denys Vlasenko355ec352018-04-02 13:34:57 +02005534 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005535 close(f);
5536 errno = EEXIST;
5537 goto ecreate;
5538 }
5539 } else {
5540 errno = EEXIST;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005541 goto ecreate;
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005542 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005543 break;
5544 }
5545 /* FALLTHROUGH */
5546 case NCLOBBER:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005547 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5548 if (f < 0)
5549 goto ecreate;
5550 break;
5551 case NAPPEND:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005552 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5553 if (f < 0)
5554 goto ecreate;
5555 break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005556 }
5557
5558 return f;
5559 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005560 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005561 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005562 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005563}
5564
5565/*
Denys Vlasenko64774602016-10-26 15:24:30 +02005566 * Copy a file descriptor to be >= 10. Throws exception on error.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005567 */
5568static int
Denys Vlasenko64774602016-10-26 15:24:30 +02005569savefd(int from)
5570{
5571 int newfd;
5572 int err;
5573
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005574 newfd = fcntl(from, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko64774602016-10-26 15:24:30 +02005575 err = newfd < 0 ? errno : 0;
5576 if (err != EBADF) {
5577 if (err)
Ron Yorstonbe366e52017-07-27 13:53:39 +01005578 ash_msg_and_raise_perror("%d", from);
Denys Vlasenko64774602016-10-26 15:24:30 +02005579 close(from);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005580 if (F_DUPFD_CLOEXEC == F_DUPFD)
5581 close_on_exec_on(newfd);
Denys Vlasenko64774602016-10-26 15:24:30 +02005582 }
5583
5584 return newfd;
5585}
5586static int
5587dup2_or_raise(int from, int to)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005588{
5589 int newfd;
5590
Denys Vlasenko64774602016-10-26 15:24:30 +02005591 newfd = (from != to) ? dup2(from, to) : to;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005592 if (newfd < 0) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005593 /* Happens when source fd is not open: try "echo >&99" */
Ron Yorstonbe366e52017-07-27 13:53:39 +01005594 ash_msg_and_raise_perror("%d", from);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005595 }
5596 return newfd;
5597}
Denys Vlasenko035486c2017-07-31 04:09:19 +02005598static int
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005599dup_CLOEXEC(int fd, int avoid_fd)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005600{
5601 int newfd;
5602 repeat:
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005603 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
5604 if (newfd >= 0) {
5605 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005606 close_on_exec_on(newfd);
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005607 } else { /* newfd < 0 */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005608 if (errno == EBUSY)
5609 goto repeat;
5610 if (errno == EINTR)
5611 goto repeat;
5612 }
5613 return newfd;
5614}
5615static int
5616xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5617{
5618 int newfd;
5619 repeat:
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005620 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005621 if (newfd < 0) {
5622 if (errno == EBUSY)
5623 goto repeat;
5624 if (errno == EINTR)
5625 goto repeat;
5626 /* fd was not open? */
5627 if (errno == EBADF)
5628 return fd;
5629 ash_msg_and_raise_perror("%d", newfd);
5630 }
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005631 if (F_DUPFD_CLOEXEC == F_DUPFD)
5632 close_on_exec_on(newfd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005633 close(fd);
5634 return newfd;
5635}
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005636
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005637/* Struct def and variable are moved down to the first usage site */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005638struct squirrel {
5639 int orig_fd;
5640 int moved_to;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005641};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005642struct redirtab {
5643 struct redirtab *next;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005644 int pair_count;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005645 struct squirrel two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005646};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005647#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005648
Denys Vlasenko035486c2017-07-31 04:09:19 +02005649static void
5650add_squirrel_closed(struct redirtab *sq, int fd)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005651{
5652 int i;
5653
Denys Vlasenko035486c2017-07-31 04:09:19 +02005654 if (!sq)
5655 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005656
Denys Vlasenko035486c2017-07-31 04:09:19 +02005657 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5658 /* If we collide with an already moved fd... */
5659 if (fd == sq->two_fd[i].orig_fd) {
5660 /* Examples:
5661 * "echo 3>FILE 3>&- 3>FILE"
5662 * "echo 3>&- 3>FILE"
5663 * No need for last redirect to insert
5664 * another "need to close 3" indicator.
5665 */
5666 TRACE(("redirect_fd %d: already moved or closed\n", fd));
5667 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005668 }
5669 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005670 TRACE(("redirect_fd %d: previous fd was closed\n", fd));
5671 sq->two_fd[i].orig_fd = fd;
5672 sq->two_fd[i].moved_to = CLOSED;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005673}
5674
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02005675static int
Denys Vlasenko035486c2017-07-31 04:09:19 +02005676save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005677{
Denys Vlasenko035486c2017-07-31 04:09:19 +02005678 int i, new_fd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005679
Denys Vlasenko035486c2017-07-31 04:09:19 +02005680 if (avoid_fd < 9) /* the important case here is that it can be -1 */
5681 avoid_fd = 9;
5682
5683#if JOBS
5684 if (fd == ttyfd) {
5685 /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */
5686 ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd);
5687 TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd));
5688 return 1; /* "we closed fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005689 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005690#endif
5691 /* Are we called from redirect(0)? E.g. redirect
5692 * in a forked child. No need to save fds,
5693 * we aren't going to use them anymore, ok to trash.
5694 */
5695 if (!sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005696 return 0;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005697
5698 /* If this one of script's fds? */
5699 if (fd != 0) {
5700 struct parsefile *pf = g_parsefile;
5701 while (pf) {
5702 /* We skip fd == 0 case because of the following:
5703 * $ ash # running ash interactively
5704 * $ . ./script.sh
5705 * and in script.sh: "exec 9>&0".
5706 * Even though top-level pf_fd _is_ 0,
5707 * it's still ok to use it: "read" builtin uses it,
5708 * why should we cripple "exec" builtin?
5709 */
5710 if (fd == pf->pf_fd) {
5711 pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
5712 return 1; /* "we closed fd" */
5713 }
5714 pf = pf->prev;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005715 }
5716 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005717
5718 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
5719
5720 /* First: do we collide with some already moved fds? */
5721 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5722 /* If we collide with an already moved fd... */
5723 if (fd == sq->two_fd[i].moved_to) {
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005724 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005725 sq->two_fd[i].moved_to = new_fd;
5726 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
5727 if (new_fd < 0) /* what? */
5728 xfunc_die();
5729 return 0; /* "we did not close fd" */
5730 }
5731 if (fd == sq->two_fd[i].orig_fd) {
5732 /* Example: echo Hello >/dev/null 1>&2 */
5733 TRACE(("redirect_fd %d: already moved\n", fd));
5734 return 0; /* "we did not close fd" */
5735 }
5736 }
5737
5738 /* If this fd is open, we move and remember it; if it's closed, new_fd = CLOSED (-1) */
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005739 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005740 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
5741 if (new_fd < 0) {
5742 if (errno != EBADF)
5743 xfunc_die();
5744 /* new_fd = CLOSED; - already is -1 */
5745 }
5746 sq->two_fd[i].moved_to = new_fd;
5747 sq->two_fd[i].orig_fd = fd;
5748
5749 /* if we move stderr, let "set -x" code know */
5750 if (fd == preverrout_fd)
5751 preverrout_fd = new_fd;
5752
5753 return 0; /* "we did not close fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005754}
5755
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005756static int
5757internally_opened_fd(int fd, struct redirtab *sq)
5758{
5759 int i;
5760#if JOBS
5761 if (fd == ttyfd)
5762 return 1;
5763#endif
5764 /* If this one of script's fds? */
5765 if (fd != 0) {
5766 struct parsefile *pf = g_parsefile;
5767 while (pf) {
5768 if (fd == pf->pf_fd)
5769 return 1;
5770 pf = pf->prev;
5771 }
5772 }
5773
5774 if (sq) for (i = 0; i < sq->pair_count && sq->two_fd[i].orig_fd != EMPTY; i++) {
5775 if (fd == sq->two_fd[i].moved_to)
5776 return 1;
5777 }
5778 return 0;
5779}
5780
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005781/*
5782 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5783 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005784 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005785 */
5786/* flags passed to redirect */
5787#define REDIR_PUSH 01 /* save previous values of file descriptors */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005788static void
5789redirect(union node *redir, int flags)
5790{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005791 struct redirtab *sv;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005792
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005793 if (!redir)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005794 return;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005795
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005796 sv = NULL;
5797 INT_OFF;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005798 if (flags & REDIR_PUSH)
5799 sv = redirlist;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005800 do {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005801 int fd;
5802 int newfd;
5803 int close_fd;
5804 int closed;
5805
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005806 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005807 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005808 //bb_error_msg("doing %d > %d", fd, newfd);
5809 newfd = redir->ndup.dupfd;
5810 close_fd = -1;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005811 } else {
5812 newfd = openredirect(redir); /* always >= 0 */
5813 if (fd == newfd) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005814 /* open() gave us precisely the fd we wanted.
5815 * This means that this fd was not busy
5816 * (not opened to anywhere).
5817 * Remember to close it on restore:
5818 */
5819 add_squirrel_closed(sv, fd);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005820 continue;
5821 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005822 close_fd = newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005823 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005824
5825 if (fd == newfd)
5826 continue;
5827
5828 /* if "N>FILE": move newfd to fd */
5829 /* if "N>&M": dup newfd to fd */
5830 /* if "N>&-": close fd (newfd is -1) */
5831
5832 IF_BASH_REDIR_OUTPUT(redirect_more:)
5833
5834 closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv);
5835 if (newfd == -1) {
5836 /* "N>&-" means "close me" */
5837 if (!closed) {
5838 /* ^^^ optimization: saving may already
5839 * have closed it. If not... */
5840 close(fd);
Denis Vlasenko22f74142008-07-24 22:34:43 +00005841 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005842 } else {
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005843 /* if newfd is a script fd or saved fd, simulate EBADF */
5844 if (internally_opened_fd(newfd, sv)) {
5845 errno = EBADF;
5846 ash_msg_and_raise_perror("%d", newfd);
5847 }
Denys Vlasenko64774602016-10-26 15:24:30 +02005848 dup2_or_raise(newfd, fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005849 if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
5850 close(close_fd);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005851#if BASH_REDIR_OUTPUT
Denys Vlasenko035486c2017-07-31 04:09:19 +02005852 if (redir->nfile.type == NTO2 && fd == 1) {
5853 /* ">&FILE". we already redirected to 1, now copy 1 to 2 */
5854 fd = 2;
5855 newfd = 1;
5856 close_fd = -1;
5857 goto redirect_more;
5858 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005859#endif
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005860 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005861 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005862 INT_ON;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005863
5864//dash:#define REDIR_SAVEFD2 03 /* set preverrout */
5865#define REDIR_SAVEFD2 0
5866 // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
5867 // triggers for pure REDIR_PUSH too. Thus, this is done almost always,
5868 // not only for calls with flags containing REDIR_SAVEFD2.
Denys Vlasenko035486c2017-07-31 04:09:19 +02005869 // We do this unconditionally (see save_fd_on_redirect()).
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005870 //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5871 // preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005872}
5873
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005874static int
5875redirectsafe(union node *redir, int flags)
5876{
5877 int err;
5878 volatile int saveint;
5879 struct jmploc *volatile savehandler = exception_handler;
5880 struct jmploc jmploc;
5881
5882 SAVE_INT(saveint);
5883 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005884 err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005885 if (!err) {
5886 exception_handler = &jmploc;
5887 redirect(redir, flags);
5888 }
5889 exception_handler = savehandler;
5890 if (err && exception_type != EXERROR)
5891 longjmp(exception_handler->loc, 1);
5892 RESTORE_INT(saveint);
5893 return err;
5894}
5895
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005896static struct redirtab*
5897pushredir(union node *redir)
5898{
5899 struct redirtab *sv;
5900 int i;
5901
5902 if (!redir)
5903 return redirlist;
5904
5905 i = 0;
5906 do {
5907 i++;
5908#if BASH_REDIR_OUTPUT
5909 if (redir->nfile.type == NTO2)
5910 i++;
5911#endif
5912 redir = redir->nfile.next;
5913 } while (redir);
5914
5915 sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
5916 sv->pair_count = i;
5917 while (--i >= 0)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005918 sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005919 sv->next = redirlist;
5920 redirlist = sv;
5921 return sv->next;
5922}
5923
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005924/*
5925 * Undo the effects of the last redirection.
5926 */
5927static void
Denys Vlasenko035486c2017-07-31 04:09:19 +02005928popredir(int drop)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005929{
5930 struct redirtab *rp;
5931 int i;
5932
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02005933 if (redirlist == NULL)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005934 return;
5935 INT_OFF;
5936 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005937 for (i = 0; i < rp->pair_count; i++) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005938 int fd = rp->two_fd[i].orig_fd;
5939 int copy = rp->two_fd[i].moved_to;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005940 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005941 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005942 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005943 continue;
5944 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005945 if (copy != EMPTY) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005946 if (!drop) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005947 /*close(fd);*/
Denys Vlasenko64774602016-10-26 15:24:30 +02005948 dup2_or_raise(copy, fd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005949 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005950 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005951 }
5952 }
5953 redirlist = rp->next;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005954 free(rp);
5955 INT_ON;
5956}
5957
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005958static void
5959unwindredir(struct redirtab *stop)
5960{
5961 while (redirlist != stop)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005962 popredir(/*drop:*/ 0);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005963}
5964
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005965
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005966/* ============ Routines to expand arguments to commands
5967 *
5968 * We have to deal with backquotes, shell variables, and file metacharacters.
5969 */
5970
Denys Vlasenko0b883582016-12-23 16:49:07 +01005971#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005972static arith_t
5973ash_arith(const char *s)
5974{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005975 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005976 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005977
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005978 math_state.lookupvar = lookupvar;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02005979 math_state.setvar = setvar0;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005980 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005981
5982 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005983 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005984 if (math_state.errmsg)
5985 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005986 INT_ON;
5987
5988 return result;
5989}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005990#endif
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01005991#if BASH_SUBSTR
5992# if ENABLE_FEATURE_SH_MATH
5993static int substr_atoi(const char *s)
5994{
5995 arith_t t = ash_arith(s);
5996 if (sizeof(t) > sizeof(int)) {
5997 /* clamp very large or very large negative nums for ${v:N:M}:
5998 * else "${v:0:0x100000001}" would work as "${v:0:1}"
5999 */
6000 if (t > INT_MAX)
6001 t = INT_MAX;
6002 if (t < INT_MIN)
6003 t = INT_MIN;
6004 }
6005 return t;
6006}
6007# else
6008# define substr_atoi(s) number(s)
6009# endif
6010#endif
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006011
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006012/*
6013 * expandarg flags
6014 */
6015#define EXP_FULL 0x1 /* perform word splitting & file globbing */
6016#define EXP_TILDE 0x2 /* do normal tilde expansion */
6017#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
6018#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
Denys Vlasenkodb74c6c2016-10-24 21:12:33 +02006019/* ^^^^^^^^^^^^^^ this is meant to support constructs such as "cmd >file*.txt"
6020 * POSIX says for this case:
6021 * Pathname expansion shall not be performed on the word by a
6022 * non-interactive shell; an interactive shell may perform it, but shall
6023 * do so only when the expansion would result in one word.
6024 * Currently, our code complies to the above rule by never globbing
6025 * redirection filenames.
6026 * Bash performs globbing, unless it is non-interactive and in POSIX mode.
6027 * (this means that on a typical Linux distro, bash almost always
6028 * performs globbing, and thus diverges from what we do).
6029 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006030#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006031#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6032#define EXP_WORD 0x40 /* expand word in parameter expansion */
Denys Vlasenko440da972018-08-05 14:29:58 +02006033#define EXP_QUOTED 0x100 /* expand word in double quotes */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006034#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
Denys Vlasenko82331882020-02-24 10:02:50 +01006035#define EXP_DISCARD 0x400 /* discard result of expansion */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006036
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006037/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006038 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006039 */
6040#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
6041#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006042#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
6043#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
6044
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006045/* Add CTLESC when necessary. */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006046#define QUOTES_ESC (EXP_FULL | EXP_CASE)
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006047
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048/*
6049 * Structure specifying which parts of the string should be searched
6050 * for IFS characters.
6051 */
6052struct ifsregion {
6053 struct ifsregion *next; /* next region in list */
6054 int begoff; /* offset of start of region */
6055 int endoff; /* offset of end of region */
6056 int nulonly; /* search for nul bytes only */
6057};
6058
6059struct arglist {
6060 struct strlist *list;
6061 struct strlist **lastp;
6062};
6063
6064/* output of current string */
6065static char *expdest;
6066/* list of back quote expressions */
6067static struct nodelist *argbackq;
6068/* first struct in list of ifs regions */
6069static struct ifsregion ifsfirst;
6070/* last struct in list */
6071static struct ifsregion *ifslastp;
6072/* holds expanded arg list */
6073static struct arglist exparg;
6074
6075/*
Denys Vlasenko455e4222016-10-27 14:45:13 +02006076 * Break the argument string into pieces based upon IFS and add the
6077 * strings to the argument list. The regions of the string to be
6078 * searched for IFS characters have been stored by recordregion.
6079 */
6080static void
6081ifsbreakup(char *string, struct arglist *arglist)
6082{
6083 struct ifsregion *ifsp;
6084 struct strlist *sp;
6085 char *start;
6086 char *p;
6087 char *q;
6088 const char *ifs, *realifs;
6089 int ifsspc;
6090 int nulonly;
6091
6092 start = string;
6093 if (ifslastp != NULL) {
6094 ifsspc = 0;
6095 nulonly = 0;
6096 realifs = ifsset() ? ifsval() : defifs;
6097 ifsp = &ifsfirst;
6098 do {
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006099 int afternul;
6100
Denys Vlasenko455e4222016-10-27 14:45:13 +02006101 p = string + ifsp->begoff;
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006102 afternul = nulonly;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006103 nulonly = ifsp->nulonly;
6104 ifs = nulonly ? nullstr : realifs;
6105 ifsspc = 0;
6106 while (p < string + ifsp->endoff) {
6107 q = p;
6108 if ((unsigned char)*p == CTLESC)
6109 p++;
6110 if (!strchr(ifs, *p)) {
6111 p++;
6112 continue;
6113 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006114 if (!(afternul || nulonly))
Denys Vlasenko455e4222016-10-27 14:45:13 +02006115 ifsspc = (strchr(defifs, *p) != NULL);
6116 /* Ignore IFS whitespace at start */
6117 if (q == start && ifsspc) {
6118 p++;
6119 start = p;
6120 continue;
6121 }
6122 *q = '\0';
6123 sp = stzalloc(sizeof(*sp));
6124 sp->text = start;
6125 *arglist->lastp = sp;
6126 arglist->lastp = &sp->next;
6127 p++;
6128 if (!nulonly) {
6129 for (;;) {
6130 if (p >= string + ifsp->endoff) {
6131 break;
6132 }
6133 q = p;
6134 if ((unsigned char)*p == CTLESC)
6135 p++;
6136 if (strchr(ifs, *p) == NULL) {
6137 p = q;
6138 break;
6139 }
6140 if (strchr(defifs, *p) == NULL) {
6141 if (ifsspc) {
6142 p++;
6143 ifsspc = 0;
6144 } else {
6145 p = q;
6146 break;
6147 }
6148 } else
6149 p++;
6150 }
6151 }
6152 start = p;
6153 } /* while */
6154 ifsp = ifsp->next;
6155 } while (ifsp != NULL);
6156 if (nulonly)
6157 goto add;
6158 }
6159
6160 if (!*start)
6161 return;
6162
6163 add:
6164 sp = stzalloc(sizeof(*sp));
6165 sp->text = start;
6166 *arglist->lastp = sp;
6167 arglist->lastp = &sp->next;
6168}
6169
6170static void
6171ifsfree(void)
6172{
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006173 struct ifsregion *p = ifsfirst.next;
6174
6175 if (!p)
6176 goto out;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006177
6178 INT_OFF;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006179 do {
6180 struct ifsregion *ifsp;
6181 ifsp = p->next;
6182 free(p);
6183 p = ifsp;
6184 } while (p);
Denys Vlasenko455e4222016-10-27 14:45:13 +02006185 ifsfirst.next = NULL;
6186 INT_ON;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006187 out:
6188 ifslastp = NULL;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006189}
6190
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006191static size_t
6192esclen(const char *start, const char *p)
6193{
6194 size_t esc = 0;
6195
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006196 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006197 esc++;
6198 }
6199 return esc;
6200}
6201
6202/*
6203 * Remove any CTLESC characters from a string.
6204 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01006205#if !BASH_PATTERN_SUBST
6206#define rmescapes(str, flag, slash_position) \
6207 rmescapes(str, flag)
6208#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006209static char *
Denys Vlasenko740058b2018-01-09 17:01:00 +01006210rmescapes(char *str, int flag, int *slash_position)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006211{
Ron Yorston417622c2015-05-18 09:59:14 +02006212 static const char qchars[] ALIGN1 = {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006213 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00006214
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006215 char *p, *q, *r;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006216 unsigned protect_against_glob;
6217 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006218
Denys Vlasenko740058b2018-01-09 17:01:00 +01006219 p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash_position));
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006220 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006221 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006222
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006223 q = p;
6224 r = str;
6225 if (flag & RMESCAPE_ALLOC) {
6226 size_t len = p - str;
6227 size_t fulllen = len + strlen(p) + 1;
6228
6229 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02006230 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006231 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02006232 /* p and str may be invalidated by makestrspace */
6233 str = (char *)stackblock() + strloc;
6234 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006235 } else if (flag & RMESCAPE_HEAP) {
6236 r = ckmalloc(fulllen);
6237 } else {
6238 r = stalloc(fulllen);
6239 }
6240 q = r;
6241 if (len > 0) {
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02006242 q = (char *)mempcpy(q, str, len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006243 }
6244 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006245
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006246 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006247 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006248 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006249 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenko216913c2018-04-02 12:35:04 +02006250// Note: protect_against_glob only affect whether
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006251// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006252 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006253 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006254 continue;
6255 }
Denys Vlasenko216913c2018-04-02 12:35:04 +02006256 if (*p == '\\') {
6257 /* naked back slash */
6258 protect_against_glob = 0;
6259 goto copy;
6260 }
Ron Yorston549deab2015-05-18 09:57:51 +02006261 if ((unsigned char)*p == CTLESC) {
6262 p++;
Denys Vlasenko13f20912016-09-25 20:54:25 +02006263#if DEBUG
6264 if (*p == '\0')
6265 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)");
6266#endif
Ron Yorston549deab2015-05-18 09:57:51 +02006267 if (protect_against_glob) {
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006268 /*
6269 * We used to trust glob() and fnmatch() to eat
6270 * superfluous escapes (\z where z has no
6271 * special meaning anyway). But this causes
6272 * bugs such as string of one greek letter rho
Denys Vlasenkoed79a632017-07-05 19:20:43 +02006273 * (unicode-encoded as two bytes "cf,81")
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006274 * getting encoded as "cf,CTLESC,81"
6275 * and here, converted to "cf,\,81" -
6276 * which does not go well with some flavors
Denys Vlasenko92b8d9c2017-07-05 19:13:44 +02006277 * of fnmatch() in unicode locales
6278 * (for example, glibc <= 2.22).
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006279 *
6280 * Lets add "\" only on the chars which need it.
Denys Vlasenko4142f012017-07-05 22:19:28 +02006281 * Testcases for less obvious chars are shown.
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006282 */
6283 if (*p == '*'
6284 || *p == '?'
6285 || *p == '['
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006286 || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */
6287 || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
6288 || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
6289 || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenko4142f012017-07-05 22:19:28 +02006290 /* Some libc support [^negate], that's why "^" also needs love */
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006291 || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006292 ) {
6293 *q++ = '\\';
6294 }
Ron Yorston549deab2015-05-18 09:57:51 +02006295 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006297#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01006298 else if (slash_position && p == str + *slash_position) {
6299 /* stop handling globbing */
6300 globbing = 0;
6301 *slash_position = q - r;
6302 slash_position = NULL;
Ron Yorston417622c2015-05-18 09:59:14 +02006303 }
6304#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006305 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006306 copy:
6307 *q++ = *p++;
6308 }
6309 *q = '\0';
6310 if (flag & RMESCAPE_GROW) {
6311 expdest = r;
6312 STADJUST(q - r + 1, expdest);
6313 }
6314 return r;
6315}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006316#define pmatch(a, b) !fnmatch((a), (b), 0)
6317
6318/*
6319 * Prepare a pattern for a expmeta (internal glob(3)) call.
6320 *
6321 * Returns an stalloced string.
6322 */
6323static char *
Ron Yorston549deab2015-05-18 09:57:51 +02006324preglob(const char *pattern, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006325{
Denys Vlasenko740058b2018-01-09 17:01:00 +01006326 return rmescapes((char *)pattern, flag | RMESCAPE_GLOB, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006327}
6328
6329/*
6330 * Put a string on the stack.
6331 */
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006332static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006333memtodest(const char *p, size_t len, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006334{
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006335 int syntax = flags & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006336 char *q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006337 char *s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006338
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006339 if (!len)
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006340 return 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006341
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006342 q = makestrspace(len * 2, expdest);
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006343 s = q;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006344
6345 do {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006346 unsigned char c = *p++;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006347 if (c) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006348 if (flags & QUOTES_ESC) {
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006349 int n = SIT(c, syntax);
6350 if (n == CCTL
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006351 || ((flags & EXP_QUOTED) && n == CBACK)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006352 ) {
6353 USTPUTC(CTLESC, q);
6354 }
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02006355 }
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006356 } else if (!(flags & EXP_KEEPNUL))
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006357 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006358 USTPUTC(c, q);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006359 } while (--len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006360
6361 expdest = q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006362 return q - s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006363}
6364
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006365static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006366strtodest(const char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006367{
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006368 size_t len = strlen(p);
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006369 memtodest(p, len, flags);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006370 return len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006371}
6372
6373/*
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006374 * Our own itoa().
6375 * cvtnum() is used even if math support is off (to prepare $? values and such).
6376 */
6377static int
6378cvtnum(arith_t num, int flags)
6379{
6380 /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
6381 /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
6382 int len = (sizeof(arith_t) >= 4) ? sizeof(arith_t) * 3 : sizeof(arith_t) * 3 + 2;
6383 char buf[len];
6384
6385 len = fmtstr(buf, len, ARITH_FMT, num);
6386 return memtodest(buf, len, flags);
6387}
6388
6389/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006390 * Record the fact that we have to scan this region of the
6391 * string for IFS characters.
6392 */
6393static void
6394recordregion(int start, int end, int nulonly)
6395{
6396 struct ifsregion *ifsp;
6397
6398 if (ifslastp == NULL) {
6399 ifsp = &ifsfirst;
6400 } else {
6401 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006402 ifsp = ckzalloc(sizeof(*ifsp));
6403 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006404 ifslastp->next = ifsp;
6405 INT_ON;
6406 }
6407 ifslastp = ifsp;
6408 ifslastp->begoff = start;
6409 ifslastp->endoff = end;
6410 ifslastp->nulonly = nulonly;
6411}
6412
6413static void
6414removerecordregions(int endoff)
6415{
6416 if (ifslastp == NULL)
6417 return;
6418
6419 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006420 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006421 struct ifsregion *ifsp;
6422 INT_OFF;
6423 ifsp = ifsfirst.next->next;
6424 free(ifsfirst.next);
6425 ifsfirst.next = ifsp;
6426 INT_ON;
6427 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006428 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006429 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006430 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006431 ifslastp = &ifsfirst;
6432 ifsfirst.endoff = endoff;
6433 }
6434 return;
6435 }
6436
6437 ifslastp = &ifsfirst;
6438 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006439 ifslastp = ifslastp->next;
6440 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 struct ifsregion *ifsp;
6442 INT_OFF;
6443 ifsp = ifslastp->next->next;
6444 free(ifslastp->next);
6445 ifslastp->next = ifsp;
6446 INT_ON;
6447 }
6448 if (ifslastp->endoff > endoff)
6449 ifslastp->endoff = endoff;
6450}
6451
6452static char *
Denys Vlasenko82331882020-02-24 10:02:50 +01006453exptilde(char *startp, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006454{
Denys Vlasenkocd716832009-11-28 22:14:02 +01006455 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006456 char *name;
6457 struct passwd *pw;
6458 const char *home;
Denys Vlasenko82331882020-02-24 10:02:50 +01006459 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006460
Denys Vlasenko82331882020-02-24 10:02:50 +01006461 p = startp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006462 name = p + 1;
6463
6464 while ((c = *++p) != '\0') {
6465 switch (c) {
6466 case CTLESC:
6467 return startp;
6468 case CTLQUOTEMARK:
6469 return startp;
6470 case ':':
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006471 if (flag & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006472 goto done;
6473 break;
6474 case '/':
6475 case CTLENDVAR:
6476 goto done;
6477 }
6478 }
6479 done:
Denys Vlasenko82331882020-02-24 10:02:50 +01006480 if (flag & EXP_DISCARD)
6481 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006482 *p = '\0';
6483 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02006484 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006485 } else {
6486 pw = getpwnam(name);
6487 if (pw == NULL)
6488 goto lose;
6489 home = pw->pw_dir;
6490 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006491 *p = c;
Denys Vlasenkoe880b1f2020-02-16 18:31:05 +01006492 if (!home)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006493 goto lose;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006494 strtodest(home, flag | EXP_QUOTED);
Denys Vlasenko82331882020-02-24 10:02:50 +01006495 out:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006496 return p;
6497 lose:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006498 return startp;
6499}
6500
6501/*
6502 * Execute a command inside back quotes. If it's a builtin command, we
6503 * want to save its output in a block obtained from malloc. Otherwise
6504 * we fork off a subprocess and get the output of the command via a pipe.
6505 * Should be called with interrupts off.
6506 */
6507struct backcmd { /* result of evalbackcmd */
6508 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006509 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006510 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006511 struct job *jp; /* job structure for command */
6512};
6513
6514/* These forward decls are needed to use "eval" code for backticks handling: */
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +02006515/* flags in argument to evaltree */
6516#define EV_EXIT 01 /* exit after evaluating tree */
6517#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02006518static int evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006519
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006520/* An evaltree() which is known to never return.
6521 * Used to use an alias:
6522 * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__));
6523 * but clang was reported to "transfer" noreturn-ness to evaltree() as well.
6524 */
6525static ALWAYS_INLINE NORETURN void
6526evaltreenr(union node *n, int flags)
6527{
6528 evaltree(n, flags);
6529 bb_unreachable(abort());
6530 /* NOTREACHED */
6531}
6532
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02006533static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006534evalbackcmd(union node *n, struct backcmd *result)
6535{
Denys Vlasenko579ad102016-10-25 21:10:20 +02006536 int pip[2];
6537 struct job *jp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006538
6539 result->fd = -1;
6540 result->buf = NULL;
6541 result->nleft = 0;
6542 result->jp = NULL;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006543 if (n == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544 goto out;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006545 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006546
Denys Vlasenko579ad102016-10-25 21:10:20 +02006547 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02006548 ash_msg_and_raise_perror("can't create pipe");
Denys Vlasenko579ad102016-10-25 21:10:20 +02006549 jp = makejob(/*n,*/ 1);
6550 if (forkshell(jp, n, FORK_NOJOB) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02006551 /* child */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006552 FORCE_INT_ON;
6553 close(pip[0]);
6554 if (pip[1] != 1) {
6555 /*close(1);*/
Denys Vlasenko64774602016-10-26 15:24:30 +02006556 dup2_or_raise(pip[1], 1);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006557 close(pip[1]);
6558 }
Denys Vlasenko960ca382016-10-25 18:12:15 +02006559/* TODO: eflag clearing makes the following not abort:
6560 * ash -c 'set -e; z=$(false;echo foo); echo $z'
6561 * which is what bash does (unless it is in POSIX mode).
6562 * dash deleted "eflag = 0" line in the commit
6563 * Date: Mon, 28 Jun 2010 17:11:58 +1000
6564 * [EVAL] Don't clear eflag in evalbackcmd
6565 * For now, preserve bash-like behavior, it seems to be somewhat more useful:
6566 */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006567 eflag = 0;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006568 ifsfree();
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006569 evaltreenr(n, EV_EXIT);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006570 /* NOTREACHED */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006571 }
Denys Vlasenko70392332016-10-27 02:31:55 +02006572 /* parent */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006573 close(pip[1]);
6574 result->fd = pip[0];
6575 result->jp = jp;
6576
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006577 out:
6578 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
6579 result->fd, result->buf, result->nleft, result->jp));
6580}
6581
6582/*
6583 * Expand stuff in backwards quotes.
6584 */
6585static void
Ron Yorston549deab2015-05-18 09:57:51 +02006586expbackq(union node *cmd, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006587{
6588 struct backcmd in;
6589 int i;
6590 char buf[128];
6591 char *p;
6592 char *dest;
6593 int startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594 struct stackmark smark;
6595
Denys Vlasenko82331882020-02-24 10:02:50 +01006596 if (flag & EXP_DISCARD)
6597 goto out;
6598
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006599 INT_OFF;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02006600 startloc = expdest - (char *)stackblock();
6601 pushstackmark(&smark, startloc);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006602 evalbackcmd(cmd, &in);
6603 popstackmark(&smark);
6604
6605 p = in.buf;
6606 i = in.nleft;
6607 if (i == 0)
6608 goto read;
6609 for (;;) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006610 memtodest(p, i, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006611 read:
6612 if (in.fd < 0)
6613 break;
Ron Yorston61d6ae22015-04-19 10:50:25 +01006614 i = nonblock_immune_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006615 TRACE(("expbackq: read returns %d\n", i));
6616 if (i <= 0)
6617 break;
6618 p = buf;
6619 }
6620
Denis Vlasenko60818682007-09-28 22:07:23 +00006621 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006622 if (in.fd >= 0) {
6623 close(in.fd);
6624 back_exitstatus = waitforjob(in.jp);
6625 }
6626 INT_ON;
6627
6628 /* Eat all trailing newlines */
6629 dest = expdest;
Denys Vlasenko9ee58922020-02-17 10:24:32 +01006630 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006631 STUNPUTC(dest);
6632 expdest = dest;
6633
Ron Yorston549deab2015-05-18 09:57:51 +02006634 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006635 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006636 TRACE(("evalbackq: size:%d:'%.*s'\n",
6637 (int)((dest - (char *)stackblock()) - startloc),
6638 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006639 stackblock() + startloc));
Denys Vlasenko82331882020-02-24 10:02:50 +01006640
6641 out:
6642 argbackq = argbackq->next;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006643}
6644
Denys Vlasenko82331882020-02-24 10:02:50 +01006645/* expari needs it */
6646static char *argstr(char *p, int flag);
6647
Denys Vlasenko0b883582016-12-23 16:49:07 +01006648#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006649/*
6650 * Expand arithmetic expression. Backup to start of expression,
6651 * evaluate, place result in (backed up) result, adjust string position.
6652 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006653static char *
6654expari(char *start, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006655{
Denys Vlasenko82331882020-02-24 10:02:50 +01006656 struct stackmark sm;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006657 int begoff;
Denys Vlasenko82331882020-02-24 10:02:50 +01006658 int endoff;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006659 int len;
Denys Vlasenko82331882020-02-24 10:02:50 +01006660 arith_t result;
6661 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006662
Denys Vlasenko82331882020-02-24 10:02:50 +01006663 p = stackblock();
6664 begoff = expdest - p;
6665 p = argstr(start, flag & EXP_DISCARD);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006666
Denys Vlasenko82331882020-02-24 10:02:50 +01006667 if (flag & EXP_DISCARD)
6668 goto out;
6669
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006670 start = stackblock();
Denys Vlasenko82331882020-02-24 10:02:50 +01006671 endoff = expdest - start;
6672 start += begoff;
6673 STADJUST(start - expdest, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006674
6675 removerecordregions(begoff);
6676
Ron Yorston549deab2015-05-18 09:57:51 +02006677 if (flag & QUOTES_ESC)
Denys Vlasenko82331882020-02-24 10:02:50 +01006678 rmescapes(start, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006679
Denys Vlasenko82331882020-02-24 10:02:50 +01006680 pushstackmark(&sm, endoff);
6681 result = ash_arith(start);
6682 popstackmark(&sm);
6683
6684 len = cvtnum(result, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006685
Ron Yorston549deab2015-05-18 09:57:51 +02006686 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006687 recordregion(begoff, begoff + len, 0);
Denys Vlasenko82331882020-02-24 10:02:50 +01006688
6689 out:
6690 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006691}
6692#endif
6693
6694/* argstr needs it */
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02006695static char *evalvar(char *p, int flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006696
6697/*
6698 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6699 * characters to allow for further processing. Otherwise treat
6700 * $@ like $* since no splitting will be performed.
6701 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006702static char *
Denys Vlasenko7f198482020-02-24 09:57:08 +01006703argstr(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006704{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006705 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006706 '=',
6707 ':',
6708 CTLQUOTEMARK,
6709 CTLENDVAR,
6710 CTLESC,
6711 CTLVAR,
6712 CTLBACKQ,
Denys Vlasenko0b883582016-12-23 16:49:07 +01006713#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006714 CTLARI,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006715 CTLENDARI,
6716#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006717 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006718 };
6719 const char *reject = spclchars;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006720 int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006721 int inquotes;
6722 size_t length;
6723 int startloc;
6724
Denys Vlasenko82331882020-02-24 10:02:50 +01006725 reject += !!(flag & EXP_VARTILDE2);
6726 reject += flag & EXP_VARTILDE ? 0 : 2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006727 inquotes = 0;
6728 length = 0;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006729 if (flag & EXP_TILDE) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006730 flag &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006731 tilde:
Denys Vlasenko82331882020-02-24 10:02:50 +01006732 if (*p == '~')
6733 p = exptilde(p, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006734 }
6735 start:
6736 startloc = expdest - (char *)stackblock();
6737 for (;;) {
Denys Vlasenko82331882020-02-24 10:02:50 +01006738 int end;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006739 unsigned char c;
6740
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006741 length += strcspn(p + length, reject);
Denys Vlasenko82331882020-02-24 10:02:50 +01006742 end = 0;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006743 c = p[length];
Denys Vlasenko82331882020-02-24 10:02:50 +01006744 if (!(c & 0x80)
6745 IF_FEATURE_SH_MATH(|| c == CTLENDARI)
6746 || c == CTLENDVAR
6747 ) {
6748 /*
6749 * c == '=' || c == ':' || c == '\0' ||
6750 * c == CTLENDARI || c == CTLENDVAR
6751 */
6752 length++;
6753 /* c == '\0' || c == CTLENDARI || c == CTLENDVAR */
6754 end = !!((c - 1) & 0x80);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006755 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006756 if (length > 0 && !(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006757 int newloc;
Denys Vlasenko82331882020-02-24 10:02:50 +01006758 char *q;
6759
6760 q = stnputs(p, length, expdest);
6761 q[-1] &= end - 1;
6762 expdest = q - (flag & EXP_WORD ? end : 0);
6763 newloc = q - (char *)stackblock() - end;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006764 if (breakall && !inquotes && newloc > startloc) {
6765 recordregion(startloc, newloc, 0);
6766 }
6767 startloc = newloc;
6768 }
6769 p += length + 1;
6770 length = 0;
6771
Denys Vlasenko82331882020-02-24 10:02:50 +01006772 if (end)
6773 break;
6774
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006775 switch (c) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006776 case '=':
Denys Vlasenko7f198482020-02-24 09:57:08 +01006777 flag |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006778 reject++;
6779 /* fall through */
6780 case ':':
6781 /*
6782 * sort of a hack - expand tildes in variable
6783 * assignments (after the first '=' and after ':'s).
6784 */
6785 if (*--p == '~') {
6786 goto tilde;
6787 }
6788 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006789 case CTLQUOTEMARK:
6790 /* "$@" syntax adherence hack */
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006791 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006792 p = evalvar(p + 1, flag | EXP_QUOTED) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006793 goto start;
6794 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006795 inquotes ^= EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006796 addquote:
Denys Vlasenko7f198482020-02-24 09:57:08 +01006797 if (flag & QUOTES_ESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006798 p--;
6799 length++;
6800 startloc++;
6801 }
6802 break;
6803 case CTLESC:
6804 startloc++;
6805 length++;
6806 goto addquote;
6807 case CTLVAR:
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006808 TRACE(("argstr: evalvar('%s')\n", p));
Denys Vlasenko7f198482020-02-24 09:57:08 +01006809 p = evalvar(p, flag | inquotes);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006810 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006811 goto start;
6812 case CTLBACKQ:
Denys Vlasenko7f198482020-02-24 09:57:08 +01006813 expbackq(argbackq->n, flag | inquotes);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006814 goto start;
Denys Vlasenko0b883582016-12-23 16:49:07 +01006815#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006816 case CTLARI:
6817 p = expari(p, flag | inquotes);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006818 goto start;
6819#endif
6820 }
6821 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006822 return p - 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006823}
6824
6825static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006826scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6827 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006828{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006829 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006830 char c;
6831
6832 loc = startp;
6833 loc2 = rmesc;
6834 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006835 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006836 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006837
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006838 c = *loc2;
6839 if (zero) {
6840 *loc2 = '\0';
6841 s = rmesc;
6842 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006843 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006844
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006845 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006846 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006847 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006848 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006849 loc++;
6850 loc++;
6851 loc2++;
6852 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006853 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006854}
6855
6856static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006857scanright(char *startp, char *rmesc, char *rmescend,
6858 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006859{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006860#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6861 int try2optimize = match_at_start;
6862#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006863 int esc = 0;
6864 char *loc;
6865 char *loc2;
6866
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006867 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6868 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6869 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6870 * Logic:
6871 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6872 * and on each iteration they go back two/one char until they reach the beginning.
6873 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6874 */
6875 /* TODO: document in what other circumstances we are called. */
6876
6877 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006878 int match;
6879 char c = *loc2;
6880 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006881 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006882 *loc2 = '\0';
6883 s = rmesc;
6884 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006885 match = pmatch(pattern, s);
6886 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006887 *loc2 = c;
6888 if (match)
6889 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006890#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6891 if (try2optimize) {
6892 /* Maybe we can optimize this:
6893 * if pattern ends with unescaped *, we can avoid checking
Denys Vlasenko10ad6222017-04-17 16:13:32 +02006894 * shorter strings: if "foo*" doesn't match "raw_value_of_v",
6895 * it won't match truncated "raw_value_of_" strings too.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006896 */
6897 unsigned plen = strlen(pattern);
6898 /* Does it end with "*"? */
6899 if (plen != 0 && pattern[--plen] == '*') {
6900 /* "xxxx*" is not escaped */
6901 /* "xxx\*" is escaped */
6902 /* "xx\\*" is not escaped */
6903 /* "x\\\*" is escaped */
6904 int slashes = 0;
6905 while (plen != 0 && pattern[--plen] == '\\')
6906 slashes++;
6907 if (!(slashes & 1))
6908 break; /* ends with unescaped "*" */
6909 }
6910 try2optimize = 0;
6911 }
6912#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006913 loc--;
6914 if (quotes) {
6915 if (--esc < 0) {
6916 esc = esclen(startp, loc);
6917 }
6918 if (esc % 2) {
6919 esc--;
6920 loc--;
6921 }
6922 }
6923 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006924 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006925}
6926
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006927static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006928static void
6929varunset(const char *end, const char *var, const char *umsg, int varflags)
6930{
6931 const char *msg;
6932 const char *tail;
6933
6934 tail = nullstr;
6935 msg = "parameter not set";
6936 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006937 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938 if (varflags & VSNUL)
6939 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006940 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006941 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006942 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006943 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006944 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006945}
6946
Denys Vlasenko82331882020-02-24 10:02:50 +01006947static char *
6948subevalvar(char *start, char *str, int strloc,
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02006949 int startloc, int varflags, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006950{
Denys Vlasenko82331882020-02-24 10:02:50 +01006951 int subtype = varflags & VSTYPE;
Ron Yorston549deab2015-05-18 09:57:51 +02006952 int quotes = flag & QUOTES_ESC;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006953 char *startp;
6954 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006955 char *rmesc, *rmescend;
Denys Vlasenko82331882020-02-24 10:02:50 +01006956 long amount;
6957 int resetloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01006958 int argstr_flags;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006959 IF_BASH_PATTERN_SUBST(int workloc;)
Denys Vlasenko740058b2018-01-09 17:01:00 +01006960 IF_BASH_PATTERN_SUBST(int slash_pos;)
6961 IF_BASH_PATTERN_SUBST(char *repl;)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006962 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006963 char *(*scan)(char*, char*, char*, char*, int, int);
Denys Vlasenko82331882020-02-24 10:02:50 +01006964 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006965
Denys Vlasenko82331882020-02-24 10:02:50 +01006966 //bb_error_msg("subevalvar(start:'%s',str:'%s',strloc:%d,startloc:%d,varflags:%x,quotes:%d)",
6967 // start, str, strloc, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006968
Denys Vlasenko740058b2018-01-09 17:01:00 +01006969#if BASH_PATTERN_SUBST
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01006970 /* For "${v/pattern/repl}", we must find the delimiter _before_
6971 * argstr() call expands possible variable references in pattern:
6972 * think about "v=a; a=a/; echo ${v/$a/r}" case.
6973 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01006974 repl = NULL;
6975 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6976 /* Find '/' and replace with NUL */
Denys Vlasenko82331882020-02-24 10:02:50 +01006977 repl = start;
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02006978 /* The pattern can't be empty.
6979 * IOW: if the first char after "${v//" is a slash,
6980 * it does not terminate the pattern - it's the first char of the pattern:
6981 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
6982 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
6983 */
6984 if (*repl == '/')
6985 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01006986 for (;;) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01006987 if (*repl == '\0') {
6988 repl = NULL;
6989 break;
6990 }
6991 if (*repl == '/') {
6992 *repl = '\0';
6993 break;
6994 }
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02006995 /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01006996 if ((unsigned char)*repl == CTLESC && repl[1])
Denys Vlasenko740058b2018-01-09 17:01:00 +01006997 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01006998 repl++;
6999 }
7000 }
7001#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007002 argstr_flags = (flag & EXP_DISCARD) | EXP_TILDE;
7003 if (!str
Denys Vlasenko216913c2018-04-02 12:35:04 +02007004#if BASH_SUBSTR
7005 && subtype != VSSUBSTR
7006#endif
7007 ) {
7008 /* EXP_CASE keeps CTLESC's */
Denys Vlasenko82331882020-02-24 10:02:50 +01007009 argstr_flags |= EXP_CASE;
Denys Vlasenko216913c2018-04-02 12:35:04 +02007010 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007011 p = argstr(start, argstr_flags);
7012
Denys Vlasenko216913c2018-04-02 12:35:04 +02007013 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
Denys Vlasenko740058b2018-01-09 17:01:00 +01007014#if BASH_PATTERN_SUBST
7015 slash_pos = -1;
7016 if (repl) {
7017 slash_pos = expdest - ((char *)stackblock() + strloc);
Denys Vlasenko883cdb72021-01-09 08:27:37 +01007018 if (!(flag & EXP_DISCARD))
7019 STPUTC('/', expdest);
Denys Vlasenko216913c2018-04-02 12:35:04 +02007020 //bb_error_msg("repl+1:'%s'", repl + 1);
Denys Vlasenko82331882020-02-24 10:02:50 +01007021 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007022 *repl = '/';
7023 }
7024#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007025 if (flag & EXP_DISCARD)
7026 return p;
7027
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007028 startp = (char *)stackblock() + startloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007029 //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007030
7031 switch (subtype) {
7032 case VSASSIGN:
Denys Vlasenko7f198482020-02-24 09:57:08 +01007033 setvar0(str, startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007034
7035 loc = startp;
7036 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007037
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007038 case VSQUESTION:
Denys Vlasenko82331882020-02-24 10:02:50 +01007039 varunset(start, str, startp, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007040 /* NOTREACHED */
7041
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007042#if BASH_SUBSTR
Denys Vlasenko826360f2017-07-17 17:49:11 +02007043 case VSSUBSTR: {
7044 int pos, len, orig_len;
7045 char *colon;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007046 char *vstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007047
Denys Vlasenko7f198482020-02-24 09:57:08 +01007048 loc = vstr = stackblock() + strloc;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007049
Denys Vlasenko826360f2017-07-17 17:49:11 +02007050 /* Read POS in ${var:POS:LEN} */
7051 colon = strchr(loc, ':');
7052 if (colon) *colon = '\0';
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007053 pos = substr_atoi(loc);
Denys Vlasenko826360f2017-07-17 17:49:11 +02007054 if (colon) *colon = ':';
7055
7056 /* Read LEN in ${var:POS:LEN} */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007057 len = vstr - startp - 1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007058 /* *loc != '\0', guaranteed by parser */
7059 if (quotes) {
7060 char *ptr;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007061 /* Adjust the length by the number of escapes */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007062 for (ptr = startp; ptr < (vstr - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007063 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007064 len--;
7065 ptr++;
7066 }
7067 }
7068 }
7069 orig_len = len;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007070 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007071 /* ${var::LEN} */
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007072 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007073 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007074 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007075 len = orig_len;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007076 while (*loc && *loc != ':')
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007077 loc++;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007078 if (*loc++ == ':')
7079 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007080 }
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007081 if (pos < 0) {
7082 /* ${VAR:$((-n)):l} starts n chars from the end */
7083 pos = orig_len + pos;
7084 }
7085 if ((unsigned)pos >= orig_len) {
7086 /* apart from obvious ${VAR:999999:l},
7087 * covers ${VAR:$((-9999999)):l} - result is ""
Denys Vlasenko826360f2017-07-17 17:49:11 +02007088 * (bash compat)
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007089 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007090 pos = 0;
7091 len = 0;
7092 }
Denys Vlasenko826360f2017-07-17 17:49:11 +02007093 if (len < 0) {
7094 /* ${VAR:N:-M} sets LEN to strlen()-M */
7095 len = (orig_len - pos) + len;
7096 }
7097 if ((unsigned)len > (orig_len - pos))
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007098 len = orig_len - pos;
7099
Denys Vlasenko7f198482020-02-24 09:57:08 +01007100 for (vstr = startp; pos; vstr++, pos--) {
7101 if (quotes && (unsigned char)*vstr == CTLESC)
7102 vstr++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007103 }
7104 for (loc = startp; len; len--) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01007105 if (quotes && (unsigned char)*vstr == CTLESC)
7106 *loc++ = *vstr++;
7107 *loc++ = *vstr++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007108 }
7109 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007110 goto out;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007111 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007112#endif /* BASH_SUBSTR */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007113 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007114
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007115 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007116
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007117#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01007118 repl = NULL;
7119
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007120 /* We'll comeback here if we grow the stack while handling
7121 * a VSREPLACE or VSREPLACEALL, since our pointers into the
7122 * stack will need rebasing, and we'll need to remove our work
7123 * areas each time
7124 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007125 restart:
7126#endif
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007127
7128 amount = expdest - ((char *)stackblock() + resetloc);
7129 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007130 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007131
7132 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007133 rmescend = (char *)stackblock() + strloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007134 //bb_error_msg("str7:'%s'", rmescend);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007135 if (quotes) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007136//TODO: how to handle slash_pos here if string changes (shortens?)
7137 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007138 if (rmesc != startp) {
7139 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007140 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007141 }
7142 }
7143 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007144 str = (char *)stackblock() + strloc;
Ron Yorston417622c2015-05-18 09:59:14 +02007145 /*
7146 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
7147 * The result is a_\_z_c (not a\_\_z_c)!
7148 *
7149 * The search pattern and replace string treat backslashes differently!
Denys Vlasenko740058b2018-01-09 17:01:00 +01007150 * "&slash_pos" causes rmescapes() to work differently on the pattern
Ron Yorston417622c2015-05-18 09:59:14 +02007151 * and string. It's only used on the first call.
7152 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007153 //bb_error_msg("str8:'%s' slash_pos:%d", str, slash_pos);
7154 rmescapes(str, RMESCAPE_GLOB,
7155 repl ? NULL : (slash_pos < 0 ? NULL : &slash_pos)
7156 );
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007157
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007158#if BASH_PATTERN_SUBST
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02007159 workloc = expdest - (char *)stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007160 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenko826360f2017-07-17 17:49:11 +02007161 int len;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007162 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007163
Denis Vlasenkod6855d12008-09-27 14:03:25 +00007164 if (!repl) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007165 //bb_error_msg("str9:'%s' slash_pos:%d", str, slash_pos);
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007166 repl = nullstr;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007167 if (slash_pos >= 0) {
7168 repl = str + slash_pos;
Ron Yorston417622c2015-05-18 09:59:14 +02007169 *repl++ = '\0';
Denys Vlasenko740058b2018-01-09 17:01:00 +01007170 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007171 }
Ron Yorston417622c2015-05-18 09:59:14 +02007172 //bb_error_msg("str:'%s' repl:'%s'", str, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007173
7174 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007175 if (str[0] == '\0')
Denys Vlasenko82331882020-02-24 10:02:50 +01007176 goto out1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007177
7178 len = 0;
7179 idx = startp;
7180 end = str - 1;
7181 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007182 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007183 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007184 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007185 if (!loc) {
7186 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007187 char *restart_detect = stackblock();
7188 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007189 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01007190 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007191 idx++;
7192 len++;
7193 STPUTC(*idx, expdest);
7194 }
7195 if (stackblock() != restart_detect)
7196 goto restart;
7197 idx++;
7198 len++;
7199 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007200 /* continue; - prone to quadratic behavior, smarter code: */
7201 if (idx >= end)
7202 break;
7203 if (str[0] == '*') {
7204 /* Pattern is "*foo". If "*foo" does not match "long_string",
7205 * it would never match "ong_string" etc, no point in trying.
7206 */
7207 goto skip_matching;
7208 }
7209 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007210 }
7211
7212 if (subtype == VSREPLACEALL) {
7213 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007214 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007215 idx++;
7216 idx++;
7217 rmesc++;
7218 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007219 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007220 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007221 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007222
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007223 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007224 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007225 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007226 if (quotes && *loc == '\\') {
7227 STPUTC(CTLESC, expdest);
7228 len++;
7229 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007230 STPUTC(*loc, expdest);
7231 if (stackblock() != restart_detect)
7232 goto restart;
7233 len++;
7234 }
7235
7236 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02007237 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007238 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007239 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007240 STPUTC(*idx, expdest);
7241 if (stackblock() != restart_detect)
7242 goto restart;
7243 len++;
7244 idx++;
7245 }
7246 break;
7247 }
7248 }
7249
7250 /* We've put the replaced text into a buffer at workloc, now
7251 * move it to the right place and adjust the stack.
7252 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007253 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007254 startp = (char *)stackblock() + startloc;
7255 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007256 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007257 loc = startp + len;
7258 goto out;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007259 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007260#endif /* BASH_PATTERN_SUBST */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007261
7262 subtype -= VSTRIMRIGHT;
7263#if DEBUG
7264 if (subtype < 0 || subtype > 7)
7265 abort();
7266#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007267 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007268 zero = subtype >> 1;
7269 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
7270 scan = (subtype & 1) ^ zero ? scanleft : scanright;
7271
7272 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
7273 if (loc) {
7274 if (zero) {
7275 memmove(startp, loc, str - loc);
7276 loc = startp + (str - loc) - 1;
7277 }
7278 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007279 } else
7280 loc = str - 1;
7281
7282 out:
7283 amount = loc - expdest;
7284 STADJUST(amount, expdest);
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007285#if BASH_PATTERN_SUBST
Denys Vlasenko82331882020-02-24 10:02:50 +01007286 out1:
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007287#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007288 /* Remove any recorded regions beyond start of variable */
7289 removerecordregions(startloc);
7290
7291 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007292}
7293
7294/*
7295 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007296 * name parameter (examples):
7297 * ash -c 'echo $1' name:'1='
7298 * ash -c 'echo $qwe' name:'qwe='
7299 * ash -c 'echo $$' name:'$='
7300 * ash -c 'echo ${$}' name:'$='
7301 * ash -c 'echo ${$##q}' name:'$=q'
7302 * ash -c 'echo ${#$}' name:'$='
7303 * note: examples with bad shell syntax:
7304 * ash -c 'echo ${#$1}' name:'$=1'
7305 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007306 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02007307static NOINLINE ssize_t
Denys Vlasenko440da972018-08-05 14:29:58 +02007308varvalue(char *name, int varflags, int flags, int quoted)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007309{
Mike Frysinger98c52642009-04-02 10:02:37 +00007310 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007311 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007312 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007313 ssize_t len = 0;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007314 int sep;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007315 int subtype = varflags & VSTYPE;
Denys Vlasenko82331882020-02-24 10:02:50 +01007316 int discard = (subtype == VSPLUS || subtype == VSLENGTH) | (flags & EXP_DISCARD);
7317
7318 if (!subtype) {
7319 if (discard)
7320 return -1;
7321
7322 raise_error_syntax("bad substitution");
7323 }
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007324
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007325 flags |= EXP_KEEPNUL;
7326 flags &= discard ? ~QUOTES_ESC : ~0;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007327 sep = (flags & EXP_FULL) << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007328
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007329 switch (*name) {
7330 case '$':
7331 num = rootpid;
7332 goto numvar;
7333 case '?':
7334 num = exitstatus;
7335 goto numvar;
7336 case '#':
7337 num = shellparam.nparam;
7338 goto numvar;
7339 case '!':
7340 num = backgndpid;
7341 if (num == 0)
7342 return -1;
7343 numvar:
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007344 len = cvtnum(num, flags);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007345 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007346 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00007347 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007348 for (i = NOPTS - 1; i >= 0; i--) {
Martijn Dekkerad4e9612018-03-31 18:15:59 +02007349 if (optlist[i] && optletters(i)) {
Mike Frysinger98c52642009-04-02 10:02:37 +00007350 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007351 len++;
7352 }
7353 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007354 check_1char_name:
7355#if 0
7356 /* handles cases similar to ${#$1} */
7357 if (name[2] != '\0')
7358 raise_error_syntax("bad substitution");
7359#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007360 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007361 case '@':
7362 if (quoted && sep)
7363 goto param;
7364 /* fall through */
7365 case '*': {
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007366 char **ap;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007367 char sepc;
Denys Vlasenko440da972018-08-05 14:29:58 +02007368 char c;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007369
Denys Vlasenko440da972018-08-05 14:29:58 +02007370 /* We will set c to 0 or ~0 depending on whether
7371 * we're doing field splitting. We won't do field
7372 * splitting if either we're quoted or sep is zero.
7373 *
7374 * Instead of testing (quoted || !sep) the following
7375 * trick optimises away any branches by using the
7376 * fact that EXP_QUOTED (which is the only bit that
7377 * can be set in quoted) is the same as EXP_FULL <<
7378 * CHAR_BIT (which is the only bit that can be set
7379 * in sep).
7380 */
7381#if EXP_QUOTED >> CHAR_BIT != EXP_FULL
7382#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT
7383#endif
7384 c = !((quoted | ~sep) & EXP_QUOTED) - 1;
7385 sep &= ~quoted;
7386 sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007387 param:
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007388 sepc = sep;
Denys Vlasenko0dd8e452016-10-01 21:02:06 +02007389 ap = shellparam.p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007390 if (!ap)
7391 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007392 while ((p = *ap++) != NULL) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007393 len += strtodest(p, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007394
7395 if (*ap && sep) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007396 len++;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007397 memtodest(&sepc, 1, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007398 }
7399 }
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007400 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007401 } /* case '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007402 case '0':
7403 case '1':
7404 case '2':
7405 case '3':
7406 case '4':
7407 case '5':
7408 case '6':
7409 case '7':
7410 case '8':
7411 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02007412 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007413 if (num < 0 || num > shellparam.nparam)
7414 return -1;
7415 p = num ? shellparam.p[num - 1] : arg0;
7416 goto value;
7417 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007418 /* NB: name has form "VAR=..." */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007419 p = lookupvar(name);
7420 value:
7421 if (!p)
7422 return -1;
7423
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007424 len = strtodest(p, flags);
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007425#if ENABLE_UNICODE_SUPPORT
7426 if (subtype == VSLENGTH && len > 0) {
7427 reinit_unicode_for_ash();
7428 if (unicode_status == UNICODE_ON) {
Ron Yorston3e3bfb82016-03-18 11:29:19 +00007429 STADJUST(-len, expdest);
7430 discard = 0;
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007431 len = unicode_strlen(p);
7432 }
7433 }
7434#endif
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007435 break;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007436 }
7437
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007438 if (discard)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007439 STADJUST(-len, expdest);
Denys Vlasenko82331882020-02-24 10:02:50 +01007440
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007441 return len;
7442}
7443
7444/*
7445 * Expand a variable, and return a pointer to the next character in the
7446 * input string.
7447 */
7448static char *
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007449evalvar(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007450{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007451 char varflags;
7452 char subtype;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007453 char *var;
7454 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007455 int startloc;
7456 ssize_t varlen;
Denys Vlasenko15558952020-02-22 19:38:40 +01007457 int discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007458 int quoted;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007459
Denys Vlasenkob0d63382009-09-16 16:18:32 +02007460 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007461 subtype = varflags & VSTYPE;
Denys Vlasenko88e15702016-10-26 01:55:56 +02007462
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007463 quoted = flag & EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007464 var = p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007465 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02007466 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007467
7468 again:
Denys Vlasenko440da972018-08-05 14:29:58 +02007469 varlen = varvalue(var, varflags, flag, quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007470 if (varflags & VSNUL)
7471 varlen--;
7472
Denys Vlasenko15558952020-02-22 19:38:40 +01007473 discard = varlen < 0 ? EXP_DISCARD : 0;
7474
Denys Vlasenko82331882020-02-24 10:02:50 +01007475 switch (subtype) {
7476 case VSPLUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007477 discard ^= EXP_DISCARD;
Denys Vlasenko82331882020-02-24 10:02:50 +01007478 /* fall through */
7479 case 0:
7480 case VSMINUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007481 p = argstr(p, flag | EXP_TILDE | EXP_WORD | (discard ^ EXP_DISCARD));
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007482 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007483
Denys Vlasenko82331882020-02-24 10:02:50 +01007484 case VSASSIGN:
7485 case VSQUESTION:
Denys Vlasenko15558952020-02-22 19:38:40 +01007486 p = subevalvar(p, var, 0, startloc, varflags,
7487 (flag & ~QUOTES_ESC) | (discard ^ EXP_DISCARD));
7488
7489 if ((flag | ~discard) & EXP_DISCARD)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007490 goto record;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007491
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007492 varflags &= ~VSNUL;
Denys Vlasenko15558952020-02-22 19:38:40 +01007493 subtype = VSNORMAL;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007494 goto again;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007495 }
7496
Denys Vlasenko15558952020-02-22 19:38:40 +01007497 if ((discard & ~flag) && uflag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007498 varunset(p, var, 0, 0);
7499
7500 if (subtype == VSLENGTH) {
Denys Vlasenko82331882020-02-24 10:02:50 +01007501 p++;
7502 if (flag & EXP_DISCARD)
7503 return p;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007504 cvtnum(varlen > 0 ? varlen : 0, flag);
Denys Vlasenko15558952020-02-22 19:38:40 +01007505 goto really_record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007506 }
7507
Denys Vlasenko82331882020-02-24 10:02:50 +01007508 if (subtype == VSNORMAL)
7509 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007510
7511#if DEBUG
7512 switch (subtype) {
7513 case VSTRIMLEFT:
7514 case VSTRIMLEFTMAX:
7515 case VSTRIMRIGHT:
7516 case VSTRIMRIGHTMAX:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007517#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007518 case VSSUBSTR:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007519#endif
7520#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007521 case VSREPLACE:
7522 case VSREPLACEALL:
7523#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007524 break;
7525 default:
7526 abort();
7527 }
7528#endif
7529
Denys Vlasenko15558952020-02-22 19:38:40 +01007530 flag |= discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007531 if (!(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007532 /*
7533 * Terminate the string and start recording the pattern
7534 * right after it
7535 */
7536 STPUTC('\0', expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007537 }
7538
Denys Vlasenko82331882020-02-24 10:02:50 +01007539 patloc = expdest - (char *)stackblock();
7540 p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
Denys Vlasenko4ace3852020-02-16 18:42:50 +01007541
Denys Vlasenko82331882020-02-24 10:02:50 +01007542 record:
Denys Vlasenko15558952020-02-22 19:38:40 +01007543 if ((flag | discard) & EXP_DISCARD)
Denys Vlasenko82331882020-02-24 10:02:50 +01007544 return p;
7545
Denys Vlasenko15558952020-02-22 19:38:40 +01007546 really_record:
Denys Vlasenko82331882020-02-24 10:02:50 +01007547 if (quoted) {
7548 quoted = *var == '@' && shellparam.nparam;
7549 if (!quoted)
7550 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007551 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007552 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007553 return p;
7554}
7555
7556/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007557 * Add a file name to the list.
7558 */
7559static void
7560addfname(const char *name)
7561{
7562 struct strlist *sp;
7563
Denis Vlasenko597906c2008-02-20 16:38:54 +00007564 sp = stzalloc(sizeof(*sp));
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02007565 sp->text = sstrdup(name);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007566 *exparg.lastp = sp;
7567 exparg.lastp = &sp->next;
7568}
7569
Felix Fietkaub5b21122017-01-31 21:58:55 +01007570/* Avoid glob() (and thus, stat() et al) for words like "echo" */
7571static int
7572hasmeta(const char *p)
7573{
7574 static const char chars[] ALIGN1 = {
7575 '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
7576 };
7577
7578 for (;;) {
7579 p = strpbrk(p, chars);
7580 if (!p)
7581 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007582 switch ((unsigned char)*p) {
Felix Fietkaub5b21122017-01-31 21:58:55 +01007583 case CTLQUOTEMARK:
7584 for (;;) {
7585 p++;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007586 if ((unsigned char)*p == CTLQUOTEMARK)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007587 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007588 if ((unsigned char)*p == CTLESC)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007589 p++;
7590 if (*p == '\0') /* huh? */
7591 return 0;
7592 }
7593 break;
7594 case '\\':
7595 case CTLESC:
7596 p++;
7597 if (*p == '\0')
7598 return 0;
7599 break;
7600 case '[':
7601 if (!strchr(p + 1, ']')) {
7602 /* It's not a properly closed [] pattern,
7603 * but other metas may follow. Continue checking.
7604 * my[file* _is_ globbed by bash
7605 * and matches filenames like "my[file1".
7606 */
7607 break;
7608 }
7609 /* fallthrough */
7610 default:
7611 /* case '*': */
7612 /* case '?': */
7613 return 1;
7614 }
7615 p++;
7616 }
7617
7618 return 0;
7619}
7620
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007621/* If we want to use glob() from libc... */
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007622#if !ENABLE_ASH_INTERNAL_GLOB
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007623
7624/* Add the result of glob() to the list */
7625static void
7626addglob(const glob_t *pglob)
7627{
7628 char **p = pglob->gl_pathv;
7629
7630 do {
7631 addfname(*p);
7632 } while (*++p);
7633}
7634static void
7635expandmeta(struct strlist *str /*, int flag*/)
7636{
7637 /* TODO - EXP_REDIR */
7638
7639 while (str) {
7640 char *p;
7641 glob_t pglob;
7642 int i;
7643
7644 if (fflag)
7645 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007646
Felix Fietkaub5b21122017-01-31 21:58:55 +01007647 if (!hasmeta(str->text))
7648 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007649
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007650 INT_OFF;
7651 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007652// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
7653// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?)
7654//
7655// glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping:
7656// if you pass it "file\?", it returns "file\?", not "file?", if no match.
7657// Which means you need to unescape the string, right? Not so fast:
7658// if there _is_ a file named "file\?" (with backslash), it is returned
7659// as "file\?" too (whichever pattern you used to find it, say, "file*").
Denys Vlasenko10ad6222017-04-17 16:13:32 +02007660// You DON'T KNOW by looking at the result whether you need to unescape it.
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007661//
7662// Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?",
7663// returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file.
7664// Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match).
7665// With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly.
7666// i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob);
7667// i = glob(p, GLOB_NOMAGIC, NULL, &pglob);
7668 i = glob(p, 0, NULL, &pglob);
7669 //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-");
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007670 if (p != str->text)
7671 free(p);
7672 switch (i) {
7673 case 0:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007674#if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007675 /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */
7676 if (!(pglob.gl_flags & GLOB_MAGCHAR))
7677 goto nometa2;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007678#endif
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007679 addglob(&pglob);
7680 globfree(&pglob);
7681 INT_ON;
7682 break;
7683 case GLOB_NOMATCH:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007684 //nometa2:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007685 globfree(&pglob);
7686 INT_ON;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007687 nometa:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007688 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007689 rmescapes(str->text, 0, NULL);
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007690 exparg.lastp = &str->next;
7691 break;
7692 default: /* GLOB_NOSPACE */
7693 globfree(&pglob);
7694 INT_ON;
7695 ash_msg_and_raise_error(bb_msg_memory_exhausted);
7696 }
7697 str = str->next;
7698 }
7699}
7700
7701#else
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007702/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007703
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007704/*
7705 * Do metacharacter (i.e. *, ?, [...]) expansion.
7706 */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007707typedef struct exp_t {
7708 char *dir;
7709 unsigned dir_max;
7710} exp_t;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007711static void
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007712expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007713{
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007714#define expdir exp->dir
7715#define expdir_max exp->dir_max
7716 char *enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007717 char *p;
7718 const char *cp;
7719 char *start;
7720 char *endname;
7721 int metaflag;
7722 struct stat statb;
7723 DIR *dirp;
7724 struct dirent *dp;
7725 int atend;
7726 int matchdot;
Ron Yorstonca25af92015-09-04 10:32:41 +01007727 int esc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007728
7729 metaflag = 0;
7730 start = name;
Ron Yorstonca25af92015-09-04 10:32:41 +01007731 for (p = name; esc = 0, *p; p += esc + 1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007732 if (*p == '*' || *p == '?')
7733 metaflag = 1;
7734 else if (*p == '[') {
7735 char *q = p + 1;
7736 if (*q == '!')
7737 q++;
7738 for (;;) {
7739 if (*q == '\\')
7740 q++;
7741 if (*q == '/' || *q == '\0')
7742 break;
7743 if (*++q == ']') {
7744 metaflag = 1;
7745 break;
7746 }
7747 }
Ron Yorstonca25af92015-09-04 10:32:41 +01007748 } else {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007749 if (*p == '\\' && p[1])
Ron Yorstonca25af92015-09-04 10:32:41 +01007750 esc++;
7751 if (p[esc] == '/') {
7752 if (metaflag)
7753 break;
7754 start = p + esc + 1;
7755 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007756 }
7757 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007758 if (metaflag == 0) { /* we've reached the end of the file name */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007759 if (!expdir_len)
7760 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007761 p = name;
7762 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007763 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007764 p++;
7765 *enddir++ = *p;
7766 } while (*p++);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007767 if (lstat(expdir, &statb) == 0)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007768 addfname(expdir);
7769 return;
7770 }
7771 endname = p;
7772 if (name < start) {
7773 p = name;
7774 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007775 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007776 p++;
7777 *enddir++ = *p++;
7778 } while (p < start);
7779 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007780 *enddir = '\0';
7781 cp = expdir;
7782 expdir_len = enddir - cp;
7783 if (!expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007784 cp = ".";
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007785 dirp = opendir(cp);
7786 if (dirp == NULL)
7787 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007788 if (*endname == 0) {
7789 atend = 1;
7790 } else {
7791 atend = 0;
Ron Yorstonca25af92015-09-04 10:32:41 +01007792 *endname = '\0';
7793 endname += esc + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007794 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007795 name_len -= endname - name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007796 matchdot = 0;
7797 p = start;
7798 if (*p == '\\')
7799 p++;
7800 if (*p == '.')
7801 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007802 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007803 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007804 continue;
7805 if (pmatch(start, dp->d_name)) {
7806 if (atend) {
7807 strcpy(enddir, dp->d_name);
7808 addfname(expdir);
7809 } else {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007810 unsigned offset;
7811 unsigned len;
7812
7813 p = stpcpy(enddir, dp->d_name);
7814 *p = '/';
7815
7816 offset = p - expdir + 1;
7817 len = offset + name_len + NAME_MAX;
7818 if (len > expdir_max) {
7819 len += PATH_MAX;
7820 expdir = ckrealloc(expdir, len);
7821 expdir_max = len;
7822 }
7823
7824 expmeta(exp, endname, name_len, offset);
7825 enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007826 }
7827 }
7828 }
7829 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007830 if (!atend)
Ron Yorstonca25af92015-09-04 10:32:41 +01007831 endname[-esc - 1] = esc ? '\\' : '/';
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007832#undef expdir
7833#undef expdir_max
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007834}
7835
7836static struct strlist *
7837msort(struct strlist *list, int len)
7838{
7839 struct strlist *p, *q = NULL;
7840 struct strlist **lpp;
7841 int half;
7842 int n;
7843
7844 if (len <= 1)
7845 return list;
7846 half = len >> 1;
7847 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007848 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007849 q = p;
7850 p = p->next;
7851 }
7852 q->next = NULL; /* terminate first half of list */
7853 q = msort(list, half); /* sort first half of list */
7854 p = msort(p, len - half); /* sort second half */
7855 lpp = &list;
7856 for (;;) {
7857#if ENABLE_LOCALE_SUPPORT
7858 if (strcoll(p->text, q->text) < 0)
7859#else
7860 if (strcmp(p->text, q->text) < 0)
7861#endif
7862 {
7863 *lpp = p;
7864 lpp = &p->next;
7865 p = *lpp;
7866 if (p == NULL) {
7867 *lpp = q;
7868 break;
7869 }
7870 } else {
7871 *lpp = q;
7872 lpp = &q->next;
7873 q = *lpp;
7874 if (q == NULL) {
7875 *lpp = p;
7876 break;
7877 }
7878 }
7879 }
7880 return list;
7881}
7882
7883/*
7884 * Sort the results of file name expansion. It calculates the number of
7885 * strings to sort and then calls msort (short for merge sort) to do the
7886 * work.
7887 */
7888static struct strlist *
7889expsort(struct strlist *str)
7890{
7891 int len;
7892 struct strlist *sp;
7893
7894 len = 0;
7895 for (sp = str; sp; sp = sp->next)
7896 len++;
7897 return msort(str, len);
7898}
7899
7900static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007901expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007902{
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007903 /* TODO - EXP_REDIR */
7904
7905 while (str) {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007906 exp_t exp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007907 struct strlist **savelastp;
7908 struct strlist *sp;
7909 char *p;
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007910 unsigned len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007911
7912 if (fflag)
7913 goto nometa;
Felix Fietkaub5b21122017-01-31 21:58:55 +01007914 if (!hasmeta(str->text))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007915 goto nometa;
7916 savelastp = exparg.lastp;
7917
7918 INT_OFF;
Ron Yorston549deab2015-05-18 09:57:51 +02007919 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007920 len = strlen(p);
7921 exp.dir_max = len + PATH_MAX;
7922 exp.dir = ckmalloc(exp.dir_max);
7923
7924 expmeta(&exp, p, len, 0);
7925 free(exp.dir);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007926 if (p != str->text)
7927 free(p);
7928 INT_ON;
7929 if (exparg.lastp == savelastp) {
7930 /*
7931 * no matches
7932 */
7933 nometa:
7934 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007935 rmescapes(str->text, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007936 exparg.lastp = &str->next;
7937 } else {
7938 *exparg.lastp = NULL;
7939 *savelastp = sp = expsort(*savelastp);
7940 while (sp->next != NULL)
7941 sp = sp->next;
7942 exparg.lastp = &sp->next;
7943 }
7944 str = str->next;
7945 }
7946}
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007947#endif /* ENABLE_ASH_INTERNAL_GLOB */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007948
7949/*
7950 * Perform variable substitution and command substitution on an argument,
7951 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7952 * perform splitting and file name expansion. When arglist is NULL, perform
7953 * here document expansion.
7954 */
7955static void
7956expandarg(union node *arg, struct arglist *arglist, int flag)
7957{
7958 struct strlist *sp;
7959 char *p;
7960
7961 argbackq = arg->narg.backquote;
7962 STARTSTACKSTR(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007963 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007964 argstr(arg->narg.text, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007965 if (arglist == NULL) {
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02007966 /* here document expanded */
7967 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007968 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007969 p = grabstackstr(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007970 TRACE(("expandarg: p:'%s'\n", p));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007971 exparg.lastp = &exparg.list;
7972 /*
7973 * TODO - EXP_REDIR
7974 */
7975 if (flag & EXP_FULL) {
7976 ifsbreakup(p, &exparg);
7977 *exparg.lastp = NULL;
7978 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007979 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007980 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +00007981 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007982 sp->text = p;
7983 *exparg.lastp = sp;
7984 exparg.lastp = &sp->next;
7985 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007986 *exparg.lastp = NULL;
7987 if (exparg.list) {
7988 *arglist->lastp = exparg.list;
7989 arglist->lastp = exparg.lastp;
7990 }
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02007991
7992 out:
7993 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007994}
7995
7996/*
7997 * Expand shell variables and backquotes inside a here document.
7998 */
7999static void
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01008000expandhere(union node *arg)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008001{
Ron Yorston549deab2015-05-18 09:57:51 +02008002 expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008003}
8004
8005/*
8006 * Returns true if the pattern matches the string.
8007 */
8008static int
8009patmatch(char *pattern, const char *string)
8010{
Denys Vlasenkobd43c672017-07-05 23:12:15 +02008011 char *p = preglob(pattern, 0);
Denys Vlasenko4476c702017-08-15 15:27:41 +02008012 int r = pmatch(p, string);
8013 //bb_error_msg("!fnmatch(pattern:'%s',str:'%s',0):%d", p, string, r);
8014 return r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008015}
8016
8017/*
8018 * See if a pattern matches in a case statement.
8019 */
8020static int
8021casematch(union node *pattern, char *val)
8022{
8023 struct stackmark smark;
8024 int result;
8025
8026 setstackmark(&smark);
8027 argbackq = pattern->narg.backquote;
8028 STARTSTACKSTR(expdest);
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008029 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008030 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008031 result = patmatch(stackblock(), val);
8032 popstackmark(&smark);
8033 return result;
8034}
8035
8036
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008037/* ============ find_command */
8038
8039struct builtincmd {
8040 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008041 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008042 /* unsigned flags; */
8043};
8044#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00008045/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008046 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008047#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008048#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008049
8050struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008051 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008052 union param {
8053 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008054 /* index >= 0 for commands without path (slashes) */
8055 /* (TODO: what exactly does the value mean? PATH position?) */
8056 /* index == -1 for commands with slashes */
8057 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008058 const struct builtincmd *cmd;
8059 struct funcnode *func;
8060 } u;
8061};
8062/* values of cmdtype */
8063#define CMDUNKNOWN -1 /* no entry in table for command */
8064#define CMDNORMAL 0 /* command is an executable program */
8065#define CMDFUNCTION 1 /* command is a shell function */
8066#define CMDBUILTIN 2 /* command is a shell builtin */
8067
8068/* action to find_command() */
8069#define DO_ERR 0x01 /* prints errors */
8070#define DO_ABS 0x02 /* checks absolute paths */
8071#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
8072#define DO_ALTPATH 0x08 /* using alternate path */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008073#define DO_REGBLTIN 0x10 /* regular built-ins and functions only */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008074
8075static void find_command(char *, struct cmdentry *, int, const char *);
8076
8077
8078/* ============ Hashing commands */
8079
8080/*
8081 * When commands are first encountered, they are entered in a hash table.
8082 * This ensures that a full path search will not have to be done for them
8083 * on each invocation.
8084 *
8085 * We should investigate converting to a linear search, even though that
8086 * would make the command name "hash" a misnomer.
8087 */
8088
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008089struct tblentry {
8090 struct tblentry *next; /* next entry in hash chain */
8091 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008092 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008093 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008094 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008095};
8096
Denis Vlasenko01631112007-12-16 17:20:38 +00008097static struct tblentry **cmdtable;
8098#define INIT_G_cmdtable() do { \
8099 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
8100} while (0)
8101
8102static int builtinloc = -1; /* index in path of %builtin, or -1 */
8103
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008104
8105static void
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008106tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008107{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008108#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008109 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00008110 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02008111 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00008112 while (*envp)
8113 putenv(*envp++);
Denys Vlasenko035486c2017-07-31 04:09:19 +02008114 popredir(/*drop:*/ 1);
Denys Vlasenko80e8e3c2017-08-07 19:24:57 +02008115 run_noexec_applet_and_exit(applet_no, cmd, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00008116 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008117 /* re-exec ourselves with the new arguments */
8118 execve(bb_busybox_exec_path, argv, envp);
8119 /* If they called chroot or otherwise made the binary no longer
8120 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008121 }
8122#endif
8123
8124 repeat:
8125#ifdef SYSV
8126 do {
8127 execve(cmd, argv, envp);
8128 } while (errno == EINTR);
8129#else
8130 execve(cmd, argv, envp);
8131#endif
Ron Yorstonca82b532018-11-01 11:45:03 +01008132
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008133 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008134 /* Run "cmd" as a shell script:
8135 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
8136 * "If the execve() function fails with ENOEXEC, the shell
8137 * shall execute a command equivalent to having a shell invoked
8138 * with the command name as its first operand,
8139 * with any remaining arguments passed to the new shell"
8140 *
8141 * That is, do not use $SHELL, user's shell, or /bin/sh;
8142 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01008143 *
8144 * Note that bash reads ~80 chars of the file, and if it sees
8145 * a zero byte before it sees newline, it doesn't try to
8146 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01008147 * message and exit code 126. For one, this prevents attempts
8148 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008149 */
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008150 argv[0] = (char*) cmd;
8151 cmd = bb_busybox_exec_path;
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008152 /* NB: this is only possible because all callers of shellexec()
8153 * ensure that the argv[-1] slot exists!
8154 */
8155 argv--;
8156 argv[0] = (char*) "ash";
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008157 goto repeat;
8158 }
8159}
8160
8161/*
8162 * Exec a program. Never returns. If you change this routine, you may
8163 * have to change the find_command routine as well.
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008164 * argv[-1] must exist and be writable! See tryexec() for why.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008165 */
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008166static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8167static void shellexec(char *prog, char **argv, const char *path, int idx)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008168{
8169 char *cmdname;
8170 int e;
8171 char **envp;
8172 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008173 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008174
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01008175 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008176 if (strchr(prog, '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008177#if ENABLE_FEATURE_SH_STANDALONE
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008178 || (applet_no = find_applet_by_name(prog)) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008179#endif
8180 ) {
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008181 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008182 if (applet_no >= 0) {
8183 /* We tried execing ourself, but it didn't work.
8184 * Maybe /proc/self/exe doesn't exist?
8185 * Try $PATH search.
8186 */
8187 goto try_PATH;
8188 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008189 e = errno;
8190 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008191 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008192 e = ENOENT;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008193 while (padvance(&path, argv[0]) >= 0) {
8194 cmdname = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008195 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00008196 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008197 if (errno != ENOENT && errno != ENOTDIR)
8198 e = errno;
8199 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008200 }
8201 }
8202
8203 /* Map to POSIX errors */
8204 switch (e) {
Denys Vlasenko2596f412018-08-05 18:04:09 +02008205 default:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008206 exerrno = 126;
8207 break;
Denys Vlasenko2596f412018-08-05 18:04:09 +02008208 case ELOOP:
8209 case ENAMETOOLONG:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008210 case ENOENT:
Denys Vlasenko2596f412018-08-05 18:04:09 +02008211 case ENOTDIR:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008212 exerrno = 127;
8213 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008214 }
8215 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008216 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008217 prog, e, suppress_int));
Denys Vlasenkof977e002020-02-20 16:54:29 +01008218 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008219 /* NOTREACHED */
8220}
8221
8222static void
8223printentry(struct tblentry *cmdp)
8224{
8225 int idx;
8226 const char *path;
8227 char *name;
8228
8229 idx = cmdp->param.index;
8230 path = pathval();
8231 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008232 padvance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008233 } while (--idx >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008234 name = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008235 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8236}
8237
8238/*
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008239 * Clear out command entries.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008240 */
8241static void
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008242clearcmdentry(void)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008243{
8244 struct tblentry **tblp;
8245 struct tblentry **pp;
8246 struct tblentry *cmdp;
8247
8248 INT_OFF;
8249 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
8250 pp = tblp;
8251 while ((cmdp = *pp) != NULL) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008252 if (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008253 || (cmdp->cmdtype == CMDBUILTIN
8254 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8255 && builtinloc > 0
8256 )
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008257 ) {
8258 *pp = cmdp->next;
8259 free(cmdp);
8260 } else {
8261 pp = &cmdp->next;
8262 }
8263 }
8264 }
8265 INT_ON;
8266}
8267
8268/*
8269 * Locate a command in the command hash table. If "add" is nonzero,
8270 * add the command to the table if it is not already present. The
8271 * variable "lastcmdentry" is set to point to the address of the link
8272 * pointing to the entry, so that delete_cmd_entry can delete the
8273 * entry.
8274 *
8275 * Interrupts must be off if called with add != 0.
8276 */
8277static struct tblentry **lastcmdentry;
8278
8279static struct tblentry *
8280cmdlookup(const char *name, int add)
8281{
8282 unsigned int hashval;
8283 const char *p;
8284 struct tblentry *cmdp;
8285 struct tblentry **pp;
8286
8287 p = name;
8288 hashval = (unsigned char)*p << 4;
8289 while (*p)
8290 hashval += (unsigned char)*p++;
8291 hashval &= 0x7FFF;
8292 pp = &cmdtable[hashval % CMDTABLESIZE];
8293 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8294 if (strcmp(cmdp->cmdname, name) == 0)
8295 break;
8296 pp = &cmdp->next;
8297 }
8298 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008299 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
8300 + strlen(name)
8301 /* + 1 - already done because
8302 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00008303 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008304 cmdp->cmdtype = CMDUNKNOWN;
8305 strcpy(cmdp->cmdname, name);
8306 }
8307 lastcmdentry = pp;
8308 return cmdp;
8309}
8310
8311/*
8312 * Delete the command entry returned on the last lookup.
8313 */
8314static void
8315delete_cmd_entry(void)
8316{
8317 struct tblentry *cmdp;
8318
8319 INT_OFF;
8320 cmdp = *lastcmdentry;
8321 *lastcmdentry = cmdp->next;
8322 if (cmdp->cmdtype == CMDFUNCTION)
8323 freefunc(cmdp->param.func);
8324 free(cmdp);
8325 INT_ON;
8326}
8327
8328/*
8329 * Add a new command entry, replacing any existing command entry for
8330 * the same name - except special builtins.
8331 */
8332static void
8333addcmdentry(char *name, struct cmdentry *entry)
8334{
8335 struct tblentry *cmdp;
8336
8337 cmdp = cmdlookup(name, 1);
8338 if (cmdp->cmdtype == CMDFUNCTION) {
8339 freefunc(cmdp->param.func);
8340 }
8341 cmdp->cmdtype = entry->cmdtype;
8342 cmdp->param = entry->u;
8343 cmdp->rehash = 0;
8344}
8345
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008346static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008347hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008348{
8349 struct tblentry **pp;
8350 struct tblentry *cmdp;
8351 int c;
8352 struct cmdentry entry;
8353 char *name;
8354
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008355 if (nextopt("r") != '\0') {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008356 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008357 return 0;
8358 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008359
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008360 if (*argptr == NULL) {
8361 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8362 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8363 if (cmdp->cmdtype == CMDNORMAL)
8364 printentry(cmdp);
8365 }
8366 }
8367 return 0;
8368 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008369
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008370 c = 0;
8371 while ((name = *argptr) != NULL) {
8372 cmdp = cmdlookup(name, 0);
8373 if (cmdp != NULL
8374 && (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008375 || (cmdp->cmdtype == CMDBUILTIN
8376 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8377 && builtinloc > 0
8378 )
8379 )
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008380 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008381 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008382 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008383 find_command(name, &entry, DO_ERR, pathval());
8384 if (entry.cmdtype == CMDUNKNOWN)
8385 c = 1;
8386 argptr++;
8387 }
8388 return c;
8389}
8390
8391/*
8392 * Called when a cd is done. Marks all commands so the next time they
8393 * are executed they will be rehashed.
8394 */
8395static void
8396hashcd(void)
8397{
8398 struct tblentry **pp;
8399 struct tblentry *cmdp;
8400
8401 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8402 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008403 if (cmdp->cmdtype == CMDNORMAL
8404 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02008405 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008406 && builtinloc > 0)
8407 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008408 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008409 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008410 }
8411 }
8412}
8413
8414/*
8415 * Fix command hash table when PATH changed.
8416 * Called before PATH is changed. The argument is the new value of PATH;
8417 * pathval() still returns the old value at this point.
8418 * Called with interrupts off.
8419 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008420static void FAST_FUNC
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008421changepath(const char *newval)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008422{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008423 const char *new;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008424 int idx;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008425 int bltin;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008426
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008427 new = newval;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008428 idx = 0;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008429 bltin = -1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008430 for (;;) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008431 if (*new == '%' && prefix(new + 1, "builtin")) {
8432 bltin = idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008433 break;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008434 }
8435 new = strchr(new, ':');
8436 if (!new)
8437 break;
8438 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02008439 new++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008440 }
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008441 builtinloc = bltin;
8442 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008443}
Ron Yorston95ebcf72015-11-03 09:42:23 +00008444enum {
8445 TEOF,
8446 TNL,
8447 TREDIR,
8448 TWORD,
8449 TSEMI,
8450 TBACKGND,
8451 TAND,
8452 TOR,
8453 TPIPE,
8454 TLP,
8455 TRP,
8456 TENDCASE,
8457 TENDBQUOTE,
8458 TNOT,
8459 TCASE,
8460 TDO,
8461 TDONE,
8462 TELIF,
8463 TELSE,
8464 TESAC,
8465 TFI,
8466 TFOR,
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008467#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +00008468 TFUNCTION,
8469#endif
8470 TIF,
8471 TIN,
8472 TTHEN,
8473 TUNTIL,
8474 TWHILE,
8475 TBEGIN,
8476 TEND
8477};
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008478typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008479
Denys Vlasenko888527c2016-10-02 16:54:17 +02008480/* Nth bit indicates if token marks the end of a list */
8481enum {
8482 tokendlist = 0
8483 /* 0 */ | (1u << TEOF)
8484 /* 1 */ | (0u << TNL)
8485 /* 2 */ | (0u << TREDIR)
8486 /* 3 */ | (0u << TWORD)
8487 /* 4 */ | (0u << TSEMI)
8488 /* 5 */ | (0u << TBACKGND)
8489 /* 6 */ | (0u << TAND)
8490 /* 7 */ | (0u << TOR)
8491 /* 8 */ | (0u << TPIPE)
8492 /* 9 */ | (0u << TLP)
8493 /* 10 */ | (1u << TRP)
8494 /* 11 */ | (1u << TENDCASE)
8495 /* 12 */ | (1u << TENDBQUOTE)
8496 /* 13 */ | (0u << TNOT)
8497 /* 14 */ | (0u << TCASE)
8498 /* 15 */ | (1u << TDO)
8499 /* 16 */ | (1u << TDONE)
8500 /* 17 */ | (1u << TELIF)
8501 /* 18 */ | (1u << TELSE)
8502 /* 19 */ | (1u << TESAC)
8503 /* 20 */ | (1u << TFI)
8504 /* 21 */ | (0u << TFOR)
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008505#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008506 /* 22 */ | (0u << TFUNCTION)
Denys Vlasenko80729a42016-10-02 22:33:15 +02008507#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008508 /* 23 */ | (0u << TIF)
8509 /* 24 */ | (0u << TIN)
8510 /* 25 */ | (1u << TTHEN)
8511 /* 26 */ | (0u << TUNTIL)
8512 /* 27 */ | (0u << TWHILE)
8513 /* 28 */ | (0u << TBEGIN)
8514 /* 29 */ | (1u << TEND)
8515 , /* thus far 29 bits used */
8516};
8517
Denys Vlasenko965b7952020-11-30 13:03:03 +01008518static const char *const tokname_array[] ALIGN_PTR = {
Denys Vlasenko888527c2016-10-02 16:54:17 +02008519 "end of file",
8520 "newline",
8521 "redirection",
8522 "word",
8523 ";",
8524 "&",
8525 "&&",
8526 "||",
8527 "|",
8528 "(",
8529 ")",
8530 ";;",
8531 "`",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008532#define KWDOFFSET 13
8533 /* the following are keywords */
Denys Vlasenko888527c2016-10-02 16:54:17 +02008534 "!",
8535 "case",
8536 "do",
8537 "done",
8538 "elif",
8539 "else",
8540 "esac",
8541 "fi",
8542 "for",
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008543#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008544 "function",
Ron Yorston95ebcf72015-11-03 09:42:23 +00008545#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008546 "if",
8547 "in",
8548 "then",
8549 "until",
8550 "while",
8551 "{",
8552 "}",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008553};
8554
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008555/* Wrapper around strcmp for qsort/bsearch/... */
8556static int
8557pstrcmp(const void *a, const void *b)
8558{
Denys Vlasenko888527c2016-10-02 16:54:17 +02008559 return strcmp((char*)a, *(char**)b);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008560}
8561
8562static const char *const *
8563findkwd(const char *s)
8564{
8565 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008566 ARRAY_SIZE(tokname_array) - KWDOFFSET,
8567 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008568}
8569
8570/*
8571 * Locate and print what a word is...
8572 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008573static int
Ron Yorston3f221112015-08-03 13:47:33 +01008574describe_command(char *command, const char *path, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008575{
8576 struct cmdentry entry;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008577#if ENABLE_ASH_ALIAS
8578 const struct alias *ap;
8579#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008580
8581 path = path ? path : pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008582
8583 if (describe_command_verbose) {
8584 out1str(command);
8585 }
8586
8587 /* First look at the keywords */
8588 if (findkwd(command)) {
8589 out1str(describe_command_verbose ? " is a shell keyword" : command);
8590 goto out;
8591 }
8592
8593#if ENABLE_ASH_ALIAS
8594 /* Then look at the aliases */
8595 ap = lookupalias(command, 0);
8596 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008597 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008598 out1str("alias ");
8599 printalias(ap);
8600 return 0;
8601 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00008602 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008603 goto out;
8604 }
8605#endif
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008606 /* Brute force */
8607 find_command(command, &entry, DO_ABS, path);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008608
8609 switch (entry.cmdtype) {
8610 case CMDNORMAL: {
8611 int j = entry.u.index;
8612 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008613 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008614 p = command;
8615 } else {
8616 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008617 padvance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008618 } while (--j >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008619 p = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008620 }
8621 if (describe_command_verbose) {
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008622 out1fmt(" is %s", p);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008623 } else {
8624 out1str(p);
8625 }
8626 break;
8627 }
8628
8629 case CMDFUNCTION:
8630 if (describe_command_verbose) {
Denys Vlasenko63c42af2018-07-24 17:08:04 +02008631 /*out1str(" is a shell function");*/
8632 out1str(" is a function"); /* bash says this */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008633 } else {
8634 out1str(command);
8635 }
8636 break;
8637
8638 case CMDBUILTIN:
8639 if (describe_command_verbose) {
8640 out1fmt(" is a %sshell builtin",
8641 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
8642 "special " : nullstr
8643 );
8644 } else {
8645 out1str(command);
8646 }
8647 break;
8648
8649 default:
8650 if (describe_command_verbose) {
8651 out1str(": not found\n");
8652 }
8653 return 127;
8654 }
8655 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01008656 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008657 return 0;
8658}
8659
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008660static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008661typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008662{
Denis Vlasenko46846e22007-05-20 13:08:31 +00008663 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008664 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00008665 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008666
Denis Vlasenko46846e22007-05-20 13:08:31 +00008667 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00008668 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008669 i++;
8670 verbose = 0;
8671 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00008672 while (argv[i]) {
Ron Yorston3f221112015-08-03 13:47:33 +01008673 err |= describe_command(argv[i++], NULL, verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008674 }
8675 return err;
8676}
8677
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008678static struct strlist *
8679fill_arglist(struct arglist *arglist, union node **argpp)
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008680{
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008681 struct strlist **lastp = arglist->lastp;
8682 union node *argp;
8683
8684 while ((argp = *argpp) != NULL) {
8685 expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
8686 *argpp = argp->narg.next;
8687 if (*lastp)
8688 break;
8689 }
8690
8691 return *lastp;
8692}
8693
Ron Yorstonda7a6db2020-02-27 09:50:18 +00008694#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008695/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
8696static int
8697parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
8698{
8699 struct strlist *sp = arglist->list;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008700 char *cp, c;
8701
8702 for (;;) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008703 sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
8704 if (!sp)
8705 return 0;
8706 cp = sp->text;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008707 if (*cp++ != '-')
8708 break;
8709 c = *cp++;
8710 if (!c)
8711 break;
8712 if (c == '-' && !*cp) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008713 if (!sp->next && !fill_arglist(arglist, argpp))
8714 return 0;
8715 sp = sp->next;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008716 break;
8717 }
8718 do {
8719 switch (c) {
8720 case 'p':
8721 *path = bb_default_path;
8722 break;
8723 default:
8724 /* run 'typecmd' for other options */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008725 return 0;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008726 }
8727 c = *cp++;
8728 } while (c);
8729 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008730
8731 arglist->list = sp;
8732 return DO_NOFUNC;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008733}
8734
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008735static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008736commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008737{
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008738 char *cmd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008739 int c;
8740 enum {
8741 VERIFY_BRIEF = 1,
8742 VERIFY_VERBOSE = 2,
8743 } verify = 0;
Ron Yorston3f221112015-08-03 13:47:33 +01008744 const char *path = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008745
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008746 /* "command [-p] PROG ARGS" (that is, without -V or -v)
8747 * never reaches this function.
8748 */
8749
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008750 while ((c = nextopt("pvV")) != '\0')
8751 if (c == 'V')
8752 verify |= VERIFY_VERBOSE;
8753 else if (c == 'v')
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008754 /*verify |= VERIFY_BRIEF*/;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008755#if DEBUG
8756 else if (c != 'p')
8757 abort();
8758#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008759 else
8760 path = bb_default_path;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008761
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00008762 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008763 cmd = *argptr;
8764 if (/*verify && */ cmd)
8765 return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008766
8767 return 0;
8768}
8769#endif
8770
8771
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008772/*static int funcblocksize; // size of structures in function */
8773/*static int funcstringsize; // size of strings in node */
Denis Vlasenko340299a2008-11-21 10:36:36 +00008774static void *funcblock; /* block to allocate function from */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008775static char *funcstring_end; /* end of block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008776
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02008777static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00008778 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8779 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
8780 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
8781 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8782 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8783 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8784 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8785 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8786 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8787 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8788 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8789 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8790 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8791 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8792 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8793 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8794 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008795#if BASH_REDIR_OUTPUT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008796 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008797#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008798 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8799 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8800 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8801 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8802 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8803 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8804 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8805 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8806 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008807};
8808
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008809static int calcsize(int funcblocksize, union node *n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008810
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008811static int
8812sizenodelist(int funcblocksize, struct nodelist *lp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008813{
8814 while (lp) {
8815 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008816 funcblocksize = calcsize(funcblocksize, lp->n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008817 lp = lp->next;
8818 }
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008819 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008820}
8821
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008822static int
8823calcsize(int funcblocksize, union node *n)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008824{
8825 if (n == NULL)
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008826 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008827 funcblocksize += nodesize[n->type];
8828 switch (n->type) {
8829 case NCMD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008830 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8831 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8832 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008833 break;
8834 case NPIPE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008835 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008836 break;
8837 case NREDIR:
8838 case NBACKGND:
8839 case NSUBSHELL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008840 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8841 funcblocksize = calcsize(funcblocksize, n->nredir.n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008842 break;
8843 case NAND:
8844 case NOR:
8845 case NSEMI:
8846 case NWHILE:
8847 case NUNTIL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008848 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8849 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008850 break;
8851 case NIF:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008852 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
8853 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
8854 funcblocksize = calcsize(funcblocksize, n->nif.test);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008855 break;
8856 case NFOR:
Denys Vlasenko561639a2016-10-07 04:28:33 +02008857 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008858 funcblocksize = calcsize(funcblocksize, n->nfor.body);
8859 funcblocksize = calcsize(funcblocksize, n->nfor.args);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008860 break;
8861 case NCASE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008862 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
8863 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008864 break;
8865 case NCLIST:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008866 funcblocksize = calcsize(funcblocksize, n->nclist.body);
8867 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
8868 funcblocksize = calcsize(funcblocksize, n->nclist.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008869 break;
8870 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008871 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
8872 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
8873 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008874 case NARG:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008875 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
Denys Vlasenko561639a2016-10-07 04:28:33 +02008876 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008877 funcblocksize = calcsize(funcblocksize, n->narg.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008878 break;
8879 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008880#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00008881 case NTO2:
8882#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008883 case NCLOBBER:
8884 case NFROM:
8885 case NFROMTO:
8886 case NAPPEND:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008887 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
8888 funcblocksize = calcsize(funcblocksize, n->nfile.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008889 break;
8890 case NTOFD:
8891 case NFROMFD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008892 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
8893 funcblocksize = calcsize(funcblocksize, n->ndup.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008894 break;
8895 case NHERE:
8896 case NXHERE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008897 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
8898 funcblocksize = calcsize(funcblocksize, n->nhere.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008899 break;
8900 case NNOT:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008901 funcblocksize = calcsize(funcblocksize, n->nnot.com);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008902 break;
8903 };
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008904 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008905}
8906
8907static char *
8908nodeckstrdup(char *s)
8909{
Denys Vlasenko561639a2016-10-07 04:28:33 +02008910 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008911 return strcpy(funcstring_end, s);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008912}
8913
8914static union node *copynode(union node *);
8915
8916static struct nodelist *
8917copynodelist(struct nodelist *lp)
8918{
8919 struct nodelist *start;
8920 struct nodelist **lpp;
8921
8922 lpp = &start;
8923 while (lp) {
8924 *lpp = funcblock;
8925 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8926 (*lpp)->n = copynode(lp->n);
8927 lp = lp->next;
8928 lpp = &(*lpp)->next;
8929 }
8930 *lpp = NULL;
8931 return start;
8932}
8933
8934static union node *
8935copynode(union node *n)
8936{
8937 union node *new;
8938
8939 if (n == NULL)
8940 return NULL;
8941 new = funcblock;
8942 funcblock = (char *) funcblock + nodesize[n->type];
8943
8944 switch (n->type) {
8945 case NCMD:
8946 new->ncmd.redirect = copynode(n->ncmd.redirect);
8947 new->ncmd.args = copynode(n->ncmd.args);
8948 new->ncmd.assign = copynode(n->ncmd.assign);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008949 new->ncmd.linno = n->ncmd.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008950 break;
8951 case NPIPE:
8952 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008953 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008954 break;
8955 case NREDIR:
8956 case NBACKGND:
8957 case NSUBSHELL:
8958 new->nredir.redirect = copynode(n->nredir.redirect);
8959 new->nredir.n = copynode(n->nredir.n);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008960 new->nredir.linno = n->nredir.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008961 break;
8962 case NAND:
8963 case NOR:
8964 case NSEMI:
8965 case NWHILE:
8966 case NUNTIL:
8967 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8968 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8969 break;
8970 case NIF:
8971 new->nif.elsepart = copynode(n->nif.elsepart);
8972 new->nif.ifpart = copynode(n->nif.ifpart);
8973 new->nif.test = copynode(n->nif.test);
8974 break;
8975 case NFOR:
8976 new->nfor.var = nodeckstrdup(n->nfor.var);
8977 new->nfor.body = copynode(n->nfor.body);
8978 new->nfor.args = copynode(n->nfor.args);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008979 new->nfor.linno = n->nfor.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008980 break;
8981 case NCASE:
8982 new->ncase.cases = copynode(n->ncase.cases);
8983 new->ncase.expr = copynode(n->ncase.expr);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008984 new->ncase.linno = n->ncase.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008985 break;
8986 case NCLIST:
8987 new->nclist.body = copynode(n->nclist.body);
8988 new->nclist.pattern = copynode(n->nclist.pattern);
8989 new->nclist.next = copynode(n->nclist.next);
8990 break;
8991 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008992 new->ndefun.body = copynode(n->ndefun.body);
8993 new->ndefun.text = nodeckstrdup(n->ndefun.text);
8994 new->ndefun.linno = n->ndefun.linno;
8995 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008996 case NARG:
8997 new->narg.backquote = copynodelist(n->narg.backquote);
8998 new->narg.text = nodeckstrdup(n->narg.text);
8999 new->narg.next = copynode(n->narg.next);
9000 break;
9001 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009002#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009003 case NTO2:
9004#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009005 case NCLOBBER:
9006 case NFROM:
9007 case NFROMTO:
9008 case NAPPEND:
9009 new->nfile.fname = copynode(n->nfile.fname);
9010 new->nfile.fd = n->nfile.fd;
9011 new->nfile.next = copynode(n->nfile.next);
9012 break;
9013 case NTOFD:
9014 case NFROMFD:
9015 new->ndup.vname = copynode(n->ndup.vname);
9016 new->ndup.dupfd = n->ndup.dupfd;
9017 new->ndup.fd = n->ndup.fd;
9018 new->ndup.next = copynode(n->ndup.next);
9019 break;
9020 case NHERE:
9021 case NXHERE:
9022 new->nhere.doc = copynode(n->nhere.doc);
9023 new->nhere.fd = n->nhere.fd;
9024 new->nhere.next = copynode(n->nhere.next);
9025 break;
9026 case NNOT:
9027 new->nnot.com = copynode(n->nnot.com);
9028 break;
9029 };
9030 new->type = n->type;
9031 return new;
9032}
9033
9034/*
9035 * Make a copy of a parse tree.
9036 */
9037static struct funcnode *
9038copyfunc(union node *n)
9039{
9040 struct funcnode *f;
9041 size_t blocksize;
9042
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009043 /*funcstringsize = 0;*/
9044 blocksize = offsetof(struct funcnode, n) + calcsize(0, n);
9045 f = ckzalloc(blocksize /* + funcstringsize */);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009046 funcblock = (char *) f + offsetof(struct funcnode, n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009047 funcstring_end = (char *) f + blocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009048 copynode(n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009049 /* f->count = 0; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009050 return f;
9051}
9052
9053/*
9054 * Define a shell function.
9055 */
9056static void
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009057defun(union node *func)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009058{
9059 struct cmdentry entry;
9060
9061 INT_OFF;
9062 entry.cmdtype = CMDFUNCTION;
9063 entry.u.func = copyfunc(func);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009064 addcmdentry(func->ndefun.text, &entry);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009065 INT_ON;
9066}
9067
Denis Vlasenko4b875702009-03-19 13:30:04 +00009068/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009069#define SKIPBREAK (1 << 0)
9070#define SKIPCONT (1 << 1)
9071#define SKIPFUNC (1 << 2)
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009072#define SKIPFUNCDEF (1 << 3)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009073static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009074static int skipcount; /* number of levels to skip */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009075static int loopnest; /* current loop nesting level */
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009076static int funcline; /* starting line number of current function, or 0 if not in a function */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009077
Denis Vlasenko4b875702009-03-19 13:30:04 +00009078/* Forward decl way out to parsing code - dotrap needs it */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009079static int evalstring(char *s, int flags);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009080
Denis Vlasenko4b875702009-03-19 13:30:04 +00009081/* Called to execute a trap.
9082 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02009083 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00009084 *
9085 * Perhaps we should avoid entering new trap handlers
9086 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009087 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009088static void
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009089dotrap(void)
9090{
Denis Vlasenko4b875702009-03-19 13:30:04 +00009091 uint8_t *g;
9092 int sig;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009093 int status, last_status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009094
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009095 if (!pending_sig)
9096 return;
9097
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009098 status = savestatus;
9099 last_status = status;
9100 if (status < 0) {
9101 status = exitstatus;
9102 savestatus = status;
9103 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009104 pending_sig = 0;
Denys Vlasenkode892052016-10-02 01:49:13 +02009105 barrier();
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009106
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009107 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009108 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009109 char *p;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009110
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009111 if (!*g)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009112 continue;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009113
9114 if (evalskip) {
9115 pending_sig = sig;
9116 break;
9117 }
9118
9119 p = trap[sig];
Denis Vlasenko4b875702009-03-19 13:30:04 +00009120 /* non-trapped SIGINT is handled separately by raise_interrupt,
9121 * don't upset it by resetting gotsig[SIGINT-1] */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009122 if (sig == SIGINT && !p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009123 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009124
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009125 TRACE(("sig %d is active, will run handler '%s'\n", sig, p));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009126 *g = 0;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009127 if (!p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009128 continue;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009129 evalstring(p, 0);
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009130 if (evalskip != SKIPFUNC)
9131 exitstatus = status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009132 }
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009133
9134 savestatus = last_status;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009135 TRACE(("dotrap returns\n"));
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009136}
9137
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009138/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009139static int evalloop(union node *, int);
9140static int evalfor(union node *, int);
9141static int evalcase(union node *, int);
9142static int evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009143static void expredir(union node *);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009144static int evalpipe(union node *, int);
9145static int evalcommand(union node *, int);
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009146static int evalbltin(const struct builtincmd *, int, char **, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009147static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009148
Eric Andersen62483552001-07-10 06:09:16 +00009149/*
Eric Andersenc470f442003-07-28 09:56:35 +00009150 * Evaluate a parse tree. The value is left in the global variable
9151 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00009152 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009153static int
Eric Andersenc470f442003-07-28 09:56:35 +00009154evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00009155{
Eric Andersenc470f442003-07-28 09:56:35 +00009156 int checkexit = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009157 int (*evalfn)(union node *, int);
Ron Yorstonf55161a2019-02-25 08:29:38 +00009158 struct stackmark smark;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009159 int status = 0;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009160
Ron Yorstonf55161a2019-02-25 08:29:38 +00009161 setstackmark(&smark);
9162
Eric Andersenc470f442003-07-28 09:56:35 +00009163 if (n == NULL) {
9164 TRACE(("evaltree(NULL) called\n"));
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02009165 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00009166 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009167 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009168
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009169 dotrap();
9170
Eric Andersenc470f442003-07-28 09:56:35 +00009171 switch (n->type) {
9172 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009173#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00009174 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009175 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00009176 break;
9177#endif
9178 case NNOT:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009179 status = !evaltree(n->nnot.com, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009180 goto setstatus;
9181 case NREDIR:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009182 errlinno = lineno = n->nredir.linno;
9183 if (funcline)
9184 lineno -= funcline - 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009185 expredir(n->nredir.redirect);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02009186 pushredir(n->nredir.redirect);
Eric Andersenc470f442003-07-28 09:56:35 +00009187 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
9188 if (!status) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009189 status = evaltree(n->nredir.n, flags & EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009190 }
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02009191 if (n->nredir.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +02009192 popredir(/*drop:*/ 0);
Eric Andersenc470f442003-07-28 09:56:35 +00009193 goto setstatus;
9194 case NCMD:
9195 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009196 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00009197 if (eflag && !(flags & EV_TESTED))
9198 checkexit = ~0;
9199 goto calleval;
9200 case NFOR:
9201 evalfn = evalfor;
9202 goto calleval;
9203 case NWHILE:
9204 case NUNTIL:
9205 evalfn = evalloop;
9206 goto calleval;
9207 case NSUBSHELL:
9208 case NBACKGND:
9209 evalfn = evalsubshell;
Denys Vlasenkocf98b0c2016-10-25 18:19:39 +02009210 goto checkexit;
Eric Andersenc470f442003-07-28 09:56:35 +00009211 case NPIPE:
9212 evalfn = evalpipe;
9213 goto checkexit;
9214 case NCASE:
9215 evalfn = evalcase;
9216 goto calleval;
9217 case NAND:
9218 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009219 case NSEMI: {
9220
Eric Andersenc470f442003-07-28 09:56:35 +00009221#if NAND + 1 != NOR
9222#error NAND + 1 != NOR
9223#endif
9224#if NOR + 1 != NSEMI
9225#error NOR + 1 != NSEMI
9226#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00009227 unsigned is_or = n->type - NAND;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009228 status = evaltree(
Eric Andersenc470f442003-07-28 09:56:35 +00009229 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009230 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00009231 );
Denys Vlasenkobc1a0082016-10-02 15:31:33 +02009232 if ((!status) == is_or || evalskip)
Eric Andersenc470f442003-07-28 09:56:35 +00009233 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009234 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009235 evaln:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009236 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009237 calleval:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009238 status = evalfn(n, flags);
9239 goto setstatus;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009240 }
Eric Andersenc470f442003-07-28 09:56:35 +00009241 case NIF:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009242 status = evaltree(n->nif.test, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009243 if (evalskip)
9244 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009245 if (!status) {
Eric Andersenc470f442003-07-28 09:56:35 +00009246 n = n->nif.ifpart;
9247 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009248 }
9249 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00009250 n = n->nif.elsepart;
9251 goto evaln;
9252 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009253 status = 0;
9254 goto setstatus;
Eric Andersenc470f442003-07-28 09:56:35 +00009255 case NDEFUN:
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009256 defun(n);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009257 /* Not necessary. To test it:
9258 * "false; f() { qwerty; }; echo $?" should print 0.
9259 */
9260 /* status = 0; */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009261 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00009262 exitstatus = status;
9263 break;
9264 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009265 out:
Denys Vlasenkob563f622010-09-25 17:15:13 +02009266 /* Order of checks below is important:
9267 * signal handlers trigger before exit caused by "set -e".
9268 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009269 dotrap();
9270
9271 if (checkexit & status)
Denys Vlasenkof977e002020-02-20 16:54:29 +01009272 raise_exception(EXEND);
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009273 if (flags & EV_EXIT)
Denys Vlasenkof977e002020-02-20 16:54:29 +01009274 raise_exception(EXEND);
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009275
Ron Yorstonf55161a2019-02-25 08:29:38 +00009276 popstackmark(&smark);
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009277 TRACE(("leaving evaltree (no interrupts)\n"));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009278 return exitstatus;
Eric Andersen62483552001-07-10 06:09:16 +00009279}
9280
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02009281static int
9282skiploop(void)
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009283{
9284 int skip = evalskip;
9285
9286 switch (skip) {
9287 case 0:
9288 break;
9289 case SKIPBREAK:
9290 case SKIPCONT:
9291 if (--skipcount <= 0) {
9292 evalskip = 0;
9293 break;
9294 }
9295 skip = SKIPBREAK;
9296 break;
9297 }
9298 return skip;
9299}
9300
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009301static int
Eric Andersenc470f442003-07-28 09:56:35 +00009302evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009303{
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009304 int skip;
Eric Andersencb57d552001-06-28 07:25:16 +00009305 int status;
9306
9307 loopnest++;
9308 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009309 flags &= EV_TESTED;
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009310 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009311 int i;
9312
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009313 i = evaltree(n->nbinary.ch1, EV_TESTED);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009314 skip = skiploop();
9315 if (skip == SKIPFUNC)
9316 status = i;
9317 if (skip)
9318 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00009319 if (n->type != NWHILE)
9320 i = !i;
9321 if (i != 0)
9322 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009323 status = evaltree(n->nbinary.ch2, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009324 skip = skiploop();
9325 } while (!(skip & ~SKIPCONT));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009326 loopnest--;
9327
9328 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009329}
9330
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009331static int
Eric Andersenc470f442003-07-28 09:56:35 +00009332evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009333{
9334 struct arglist arglist;
9335 union node *argp;
9336 struct strlist *sp;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009337 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009338
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009339 errlinno = lineno = n->ncase.linno;
9340 if (funcline)
9341 lineno -= funcline - 1;
9342
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009343 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009344 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009345 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Ron Yorston549deab2015-05-18 09:57:51 +02009346 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
Eric Andersencb57d552001-06-28 07:25:16 +00009347 }
9348 *arglist.lastp = NULL;
9349
Eric Andersencb57d552001-06-28 07:25:16 +00009350 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009351 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009352 for (sp = arglist.list; sp; sp = sp->next) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009353 setvar0(n->nfor.var, sp->text);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009354 status = evaltree(n->nfor.body, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009355 if (skiploop() & ~SKIPCONT)
Eric Andersencb57d552001-06-28 07:25:16 +00009356 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009357 }
9358 loopnest--;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009359
9360 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009361}
9362
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009363static int
Eric Andersenc470f442003-07-28 09:56:35 +00009364evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009365{
9366 union node *cp;
9367 union node *patp;
9368 struct arglist arglist;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009369 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009370
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009371 errlinno = lineno = n->ncase.linno;
9372 if (funcline)
9373 lineno -= funcline - 1;
9374
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009375 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009376 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00009377 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009378 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
9379 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00009380 if (casematch(patp, arglist.list->text)) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009381 /* Ensure body is non-empty as otherwise
9382 * EV_EXIT may prevent us from setting the
9383 * exit status.
9384 */
9385 if (evalskip == 0 && cp->nclist.body) {
9386 status = evaltree(cp->nclist.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00009387 }
9388 goto out;
9389 }
9390 }
9391 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009392 out:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009393 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009394}
9395
Eric Andersenc470f442003-07-28 09:56:35 +00009396/*
9397 * Kick off a subshell to evaluate a tree.
9398 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009399static int
Eric Andersenc470f442003-07-28 09:56:35 +00009400evalsubshell(union node *n, int flags)
9401{
9402 struct job *jp;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009403 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
Eric Andersenc470f442003-07-28 09:56:35 +00009404 int status;
9405
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009406 errlinno = lineno = n->nredir.linno;
9407 if (funcline)
9408 lineno -= funcline - 1;
9409
Eric Andersenc470f442003-07-28 09:56:35 +00009410 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02009411 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00009412 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009413 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009414 if (backgnd == FORK_FG)
9415 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009416 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009417 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009418 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009419 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009420 flags |= EV_EXIT;
9421 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02009422 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009423 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00009424 redirect(n->nredir.redirect, 0);
9425 evaltreenr(n->nredir.n, flags);
9426 /* never returns */
9427 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009428 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009429 status = 0;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009430 if (backgnd == FORK_FG)
Eric Andersenc470f442003-07-28 09:56:35 +00009431 status = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009432 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009433 return status;
Eric Andersenc470f442003-07-28 09:56:35 +00009434}
9435
Eric Andersenc470f442003-07-28 09:56:35 +00009436/*
9437 * Compute the names of the files in a redirection list.
9438 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009439static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00009440static void
9441expredir(union node *n)
9442{
9443 union node *redir;
9444
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009445 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009446 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009447
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009448 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009449 fn.lastp = &fn.list;
9450 switch (redir->type) {
9451 case NFROMTO:
9452 case NFROM:
9453 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009454#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009455 case NTO2:
9456#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009457 case NCLOBBER:
9458 case NAPPEND:
9459 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02009460 TRACE(("expredir expanded to '%s'\n", fn.list->text));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009461#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009462 store_expfname:
9463#endif
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009464#if 0
9465// By the design of stack allocator, the loop of this kind:
9466// while true; do while true; do break; done </dev/null; done
9467// will look like a memory leak: ash plans to free expfname's
9468// of "/dev/null" as soon as it finishes running the loop
9469// (in this case, never).
9470// This "fix" is wrong:
Jon Tollefson4ba6c5d2012-11-13 19:26:53 +01009471 if (redir->nfile.expfname)
9472 stunalloc(redir->nfile.expfname);
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009473// It results in corrupted state of stacked allocations.
9474#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009475 redir->nfile.expfname = fn.list->text;
9476 break;
9477 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00009478 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00009479 if (redir->ndup.vname) {
Denys Vlasenkoe368d852020-02-16 19:02:22 +01009480 expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009481 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009482 ash_msg_and_raise_error("redir error");
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009483#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009484 if (!isdigit_str9(fn.list->text)) {
9485 /* >&file, not >&fd */
9486 if (redir->nfile.fd != 1) /* 123>&file - BAD */
9487 ash_msg_and_raise_error("redir error");
9488 redir->type = NTO2;
9489 goto store_expfname;
9490 }
9491#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009492 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009493 }
9494 break;
9495 }
9496 }
9497}
9498
Eric Andersencb57d552001-06-28 07:25:16 +00009499/*
Eric Andersencb57d552001-06-28 07:25:16 +00009500 * Evaluate a pipeline. All the processes in the pipeline are children
9501 * of the process creating the pipeline. (This differs from some versions
9502 * of the shell, which make the last process in a pipeline the parent
9503 * of all the rest.)
9504 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009505static int
Eric Andersenc470f442003-07-28 09:56:35 +00009506evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009507{
9508 struct job *jp;
9509 struct nodelist *lp;
9510 int pipelen;
9511 int prevfd;
9512 int pip[2];
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009513 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009514
Eric Andersenc470f442003-07-28 09:56:35 +00009515 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00009516 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009517 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00009518 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009519 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009520 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009521 if (n->npipe.pipe_backgnd == 0)
9522 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009523 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00009524 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009525 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009526 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00009527 pip[1] = -1;
9528 if (lp->next) {
9529 if (pipe(pip) < 0) {
9530 close(prevfd);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02009531 ash_msg_and_raise_perror("can't create pipe");
Eric Andersencb57d552001-06-28 07:25:16 +00009532 }
9533 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009534 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02009535 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009536 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009537 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009538 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00009539 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009540 if (prevfd > 0) {
9541 dup2(prevfd, 0);
9542 close(prevfd);
9543 }
9544 if (pip[1] > 1) {
9545 dup2(pip[1], 1);
9546 close(pip[1]);
9547 }
Eric Andersenc470f442003-07-28 09:56:35 +00009548 evaltreenr(lp->n, flags);
9549 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00009550 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009551 /* parent */
Eric Andersencb57d552001-06-28 07:25:16 +00009552 if (prevfd >= 0)
9553 close(prevfd);
9554 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00009555 /* Don't want to trigger debugging */
9556 if (pip[1] != -1)
9557 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009558 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009559 if (n->npipe.pipe_backgnd == 0) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009560 status = waitforjob(jp);
9561 TRACE(("evalpipe: job done exit status %d\n", status));
Eric Andersencb57d552001-06-28 07:25:16 +00009562 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009563 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009564
9565 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009566}
9567
Ron Yorston9e2a5662020-01-21 16:01:58 +00009568/* setinteractive needs this forward reference */
9569#if EDITING_HAS_get_exe_name
9570static const char *get_builtin_name(int i) FAST_FUNC;
9571#endif
9572
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009573/*
9574 * Controls whether the shell is interactive or not.
9575 */
9576static void
9577setinteractive(int on)
9578{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009579 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009580
9581 if (++on == is_interactive)
9582 return;
9583 is_interactive = on;
9584 setsignal(SIGINT);
9585 setsignal(SIGQUIT);
9586 setsignal(SIGTERM);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009587 if (is_interactive > 1) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009588#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009589 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009590 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009591
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009592 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009593 /* note: ash and hush share this string */
9594 out1fmt("\n\n%s %s\n"
Denys Vlasenko2ec34962014-09-08 16:52:39 +02009595 IF_ASH_HELP("Enter 'help' for a list of built-in commands.\n")
9596 "\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009597 bb_banner,
9598 "built-in shell (ash)"
9599 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009600 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009601 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009602#endif
Denys Vlasenko897475a2019-06-01 16:35:09 +02009603#if ENABLE_FEATURE_EDITING
Ron Yorston9e2a5662020-01-21 16:01:58 +00009604 if (!line_input_state) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009605 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
Ron Yorston9e2a5662020-01-21 16:01:58 +00009606# if EDITING_HAS_get_exe_name
9607 line_input_state->get_exe_name = get_builtin_name;
9608# endif
9609 }
Denys Vlasenko897475a2019-06-01 16:35:09 +02009610#endif
9611 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009612}
9613
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009614static void
9615optschanged(void)
9616{
9617#if DEBUG
9618 opentrace();
9619#endif
9620 setinteractive(iflag);
9621 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009622#if ENABLE_FEATURE_EDITING_VI
Denys Vlasenko897475a2019-06-01 16:35:09 +02009623 if (line_input_state) {
9624 if (viflag)
9625 line_input_state->flags |= VI_MODE;
9626 else
9627 line_input_state->flags &= ~VI_MODE;
9628 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009629#else
9630 viflag = 0; /* forcibly keep the option off */
9631#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009632}
9633
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009634struct localvar_list {
9635 struct localvar_list *next;
9636 struct localvar *lv;
9637};
9638
9639static struct localvar_list *localvar_stack;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009640
9641/*
9642 * Called after a function returns.
9643 * Interrupts must be off.
9644 */
9645static void
Denys Vlasenko981a0562017-07-26 19:53:11 +02009646poplocalvars(int keep)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009647{
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009648 struct localvar_list *ll;
9649 struct localvar *lvp, *next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009650 struct var *vp;
9651
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009652 INT_OFF;
9653 ll = localvar_stack;
9654 localvar_stack = ll->next;
9655
9656 next = ll->lv;
9657 free(ll);
9658
9659 while ((lvp = next) != NULL) {
9660 next = lvp->next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009661 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02009662 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denys Vlasenko981a0562017-07-26 19:53:11 +02009663 if (keep) {
9664 int bits = VSTRFIXED;
9665
9666 if (lvp->flags != VUNSET) {
9667 if (vp->var_text == lvp->text)
9668 bits |= VTEXTFIXED;
9669 else if (!(lvp->flags & (VTEXTFIXED|VSTACK)))
9670 free((char*)lvp->text);
9671 }
9672
9673 vp->flags &= ~bits;
9674 vp->flags |= (lvp->flags & bits);
9675
9676 if ((vp->flags &
9677 (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
9678 unsetvar(vp->var_text);
9679 } else if (vp == NULL) { /* $- saved */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009680 memcpy(optlist, lvp->text, sizeof(optlist));
9681 free((char*)lvp->text);
9682 optschanged();
Denys Vlasenkod5b500c2017-07-26 19:25:40 +02009683 } else if (lvp->flags == VUNSET) {
9684 vp->flags &= ~(VSTRFIXED|VREADONLY);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009685 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009686 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009687 if (vp->var_func)
9688 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009689 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009690 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009691 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009692 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009693 }
9694 free(lvp);
9695 }
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009696 INT_ON;
9697}
9698
9699/*
9700 * Create a new localvar environment.
9701 */
Denys Vlasenko484fc202017-07-26 19:55:31 +02009702static struct localvar_list *
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009703pushlocalvars(int push)
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009704{
9705 struct localvar_list *ll;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009706 struct localvar_list *top;
9707
9708 top = localvar_stack;
9709 if (!push)
9710 goto out;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009711
9712 INT_OFF;
9713 ll = ckzalloc(sizeof(*ll));
9714 /*ll->lv = NULL; - zalloc did it */
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009715 ll->next = top;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009716 localvar_stack = ll;
9717 INT_ON;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009718 out:
9719 return top;
Denys Vlasenko484fc202017-07-26 19:55:31 +02009720}
9721
9722static void
9723unwindlocalvars(struct localvar_list *stop)
9724{
9725 while (localvar_stack != stop)
9726 poplocalvars(0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009727}
9728
9729static int
9730evalfun(struct funcnode *func, int argc, char **argv, int flags)
9731{
9732 volatile struct shparam saveparam;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009733 struct jmploc *volatile savehandler;
9734 struct jmploc jmploc;
9735 int e;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009736 int savefuncline;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009737
9738 saveparam = shellparam;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009739 savefuncline = funcline;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +02009740 savehandler = exception_handler;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009741 e = setjmp(jmploc.loc);
9742 if (e) {
9743 goto funcdone;
9744 }
9745 INT_OFF;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009746 exception_handler = &jmploc;
Denis Vlasenko01631112007-12-16 17:20:38 +00009747 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009748 func->count++;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009749 funcline = func->n.ndefun.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009750 INT_ON;
9751 shellparam.nparam = argc - 1;
9752 shellparam.p = argv + 1;
9753#if ENABLE_ASH_GETOPTS
9754 shellparam.optind = 1;
9755 shellparam.optoff = -1;
9756#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009757 evaltree(func->n.ndefun.body, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00009758 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009759 INT_OFF;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009760 funcline = savefuncline;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009761 freefunc(func);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009762 freeparam(&shellparam);
9763 shellparam = saveparam;
9764 exception_handler = savehandler;
9765 INT_ON;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009766 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009767 return e;
9768}
9769
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009770/*
9771 * Make a variable a local variable. When a variable is made local, it's
9772 * value and flags are saved in a localvar structure. The saved values
9773 * will be restored when the shell function returns. We handle the name
Denys Vlasenkoe0a4e102015-05-13 02:20:14 +02009774 * "-" as a special case: it makes changes to "set +-options" local
9775 * (options will be restored on return from the function).
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009776 */
9777static void
Denys Vlasenko3e729102020-02-19 17:33:44 +01009778mklocal(char *name, int flags)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009779{
9780 struct localvar *lvp;
9781 struct var **vpp;
9782 struct var *vp;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009783 char *eq = strchr(name, '=');
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009784
9785 INT_OFF;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009786 /* Cater for duplicate "local". Examples:
9787 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
9788 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
9789 */
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009790 lvp = localvar_stack->lv;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009791 while (lvp) {
Eugene Rudoy1285aa62015-04-26 23:32:00 +02009792 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009793 if (eq)
9794 setvareq(name, 0);
9795 /* else:
9796 * it's a duplicate "local VAR" declaration, do nothing
9797 */
Denys Vlasenko06b11492016-11-04 16:43:18 +01009798 goto ret;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009799 }
9800 lvp = lvp->next;
9801 }
9802
9803 lvp = ckzalloc(sizeof(*lvp));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009804 if (LONE_DASH(name)) {
9805 char *p;
9806 p = ckmalloc(sizeof(optlist));
9807 lvp->text = memcpy(p, optlist, sizeof(optlist));
9808 vp = NULL;
9809 } else {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009810 vpp = hashvar(name);
9811 vp = *findvar(vpp, name);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009812 if (vp == NULL) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009813 /* variable did not exist yet */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009814 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +01009815 vp = setvareq(name, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009816 else
Denys Vlasenko3e729102020-02-19 17:33:44 +01009817 vp = setvar(name, NULL, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009818 lvp->flags = VUNSET;
9819 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009820 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009821 lvp->flags = vp->flags;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009822 /* make sure neither "struct var" nor string gets freed
9823 * during (un)setting:
9824 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009825 vp->flags |= VSTRFIXED|VTEXTFIXED;
9826 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +01009827 setvareq(name, flags);
Denys Vlasenko109ee5d2014-03-16 18:41:11 +01009828 else
9829 /* "local VAR" unsets VAR: */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009830 setvar0(name, NULL);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009831 }
9832 }
9833 lvp->vp = vp;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009834 lvp->next = localvar_stack->lv;
9835 localvar_stack->lv = lvp;
Denys Vlasenko06b11492016-11-04 16:43:18 +01009836 ret:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009837 INT_ON;
9838}
9839
9840/*
9841 * The "local" command.
9842 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009843static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009844localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009845{
9846 char *name;
9847
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009848 if (!localvar_stack)
Ron Yorstonef2386b2015-10-29 16:19:14 +00009849 ash_msg_and_raise_error("not in a function");
9850
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009851 argv = argptr;
9852 while ((name = *argv++) != NULL) {
Denys Vlasenko3e729102020-02-19 17:33:44 +01009853 mklocal(name, 0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009854 }
9855 return 0;
9856}
9857
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009858static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009859falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009860{
9861 return 1;
9862}
9863
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009864static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009865truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009866{
9867 return 0;
9868}
9869
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009870static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009871execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009872{
Denys Vlasenko6c149f42017-04-12 21:31:32 +02009873 optionarg = NULL;
9874 while (nextopt("a:") != '\0')
9875 /* nextopt() sets optionarg to "-a ARGV0" */;
9876
9877 argv = argptr;
9878 if (argv[0]) {
9879 char *prog;
9880
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009881 iflag = 0; /* exit on error */
9882 mflag = 0;
9883 optschanged();
Denys Vlasenkoe5814a52016-07-16 18:33:55 +02009884 /* We should set up signals for "exec CMD"
9885 * the same way as for "CMD" without "exec".
9886 * But optschanged->setinteractive->setsignal
9887 * still thought we are a root shell. Therefore, for example,
9888 * SIGQUIT is still set to IGN. Fix it:
9889 */
9890 shlvl++;
9891 setsignal(SIGQUIT);
9892 /*setsignal(SIGTERM); - unnecessary because of iflag=0 */
9893 /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */
9894 /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */
9895
Denys Vlasenko6c149f42017-04-12 21:31:32 +02009896 prog = argv[0];
9897 if (optionarg)
9898 argv[0] = optionarg;
9899 shellexec(prog, argv, pathval(), 0);
Denys Vlasenkoe5814a52016-07-16 18:33:55 +02009900 /* NOTREACHED */
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009901 }
9902 return 0;
9903}
9904
9905/*
9906 * The return command.
9907 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009908static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009909returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009910{
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009911 int skip;
9912 int status;
9913
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009914 /*
9915 * If called outside a function, do what ksh does;
9916 * skip the rest of the file.
9917 */
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009918 if (argv[1]) {
9919 skip = SKIPFUNC;
9920 status = number(argv[1]);
9921 } else {
9922 skip = SKIPFUNCDEF;
9923 status = exitstatus;
9924 }
9925 evalskip = skip;
9926
9927 return status;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009928}
9929
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009930/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009931static int breakcmd(int, char **) FAST_FUNC;
9932static int dotcmd(int, char **) FAST_FUNC;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009933static int evalcmd(int, char **, int) FAST_FUNC;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009934static int exitcmd(int, char **) FAST_FUNC;
9935static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009936#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009937static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009938#endif
Denys Vlasenko2ec34962014-09-08 16:52:39 +02009939#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009940static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009941#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +02009942#if MAX_HISTORY
9943static int historycmd(int, char **) FAST_FUNC;
9944#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01009945#if ENABLE_FEATURE_SH_MATH
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009946static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009947#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009948static int readcmd(int, char **) FAST_FUNC;
9949static int setcmd(int, char **) FAST_FUNC;
9950static int shiftcmd(int, char **) FAST_FUNC;
9951static int timescmd(int, char **) FAST_FUNC;
9952static int trapcmd(int, char **) FAST_FUNC;
9953static int umaskcmd(int, char **) FAST_FUNC;
9954static int unsetcmd(int, char **) FAST_FUNC;
9955static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009956
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009957#define BUILTIN_NOSPEC "0"
9958#define BUILTIN_SPECIAL "1"
9959#define BUILTIN_REGULAR "2"
9960#define BUILTIN_SPEC_REG "3"
9961#define BUILTIN_ASSIGN "4"
9962#define BUILTIN_SPEC_ASSG "5"
9963#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009964#define BUILTIN_SPEC_REG_ASSG "7"
9965
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009966/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko265062d2017-01-10 15:13:30 +01009967#if ENABLE_ASH_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009968static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009969#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +01009970#if ENABLE_ASH_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009971static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009972#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009973#if ENABLE_ASH_TEST || BASH_TEST2
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009974static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009975#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009976
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009977/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009978static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009979 { BUILTIN_SPEC_REG "." , dotcmd },
9980 { BUILTIN_SPEC_REG ":" , truecmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +01009981#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009982 { BUILTIN_REGULAR "[" , testcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009983#endif
9984#if BASH_TEST2
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009985 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009986#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009987#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009988 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009989#endif
9990#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009991 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009992#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009993 { BUILTIN_SPEC_REG "break" , breakcmd },
9994 { BUILTIN_REGULAR "cd" , cdcmd },
9995 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009996#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009997 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009998#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009999 { BUILTIN_SPEC_REG "continue", breakcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010000#if ENABLE_ASH_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010001 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010002#endif
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010003 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010004 { BUILTIN_SPEC_REG "exec" , execcmd },
10005 { BUILTIN_SPEC_REG "exit" , exitcmd },
10006 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
10007 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010008#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010009 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010010#endif
10011#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010012 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010013#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010014 { BUILTIN_REGULAR "hash" , hashcmd },
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010015#if ENABLE_ASH_HELP
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010016 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010017#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010018#if MAX_HISTORY
10019 { BUILTIN_NOSPEC "history" , historycmd },
10020#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010021#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010022 { BUILTIN_REGULAR "jobs" , jobscmd },
10023 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010024#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010025#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010026 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010027#endif
Denys Vlasenko85241c72017-07-26 20:00:08 +020010028 { BUILTIN_SPEC_REG_ASSG "local" , localcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010029#if ENABLE_ASH_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010030 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +000010031#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010032 { BUILTIN_REGULAR "pwd" , pwdcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010033 { BUILTIN_REGULAR "read" , readcmd },
10034 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
10035 { BUILTIN_SPEC_REG "return" , returncmd },
10036 { BUILTIN_SPEC_REG "set" , setcmd },
10037 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010038#if BASH_SOURCE
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010039 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +020010040#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010041#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010042 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010043#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010044 { BUILTIN_SPEC_REG "times" , timescmd },
10045 { BUILTIN_SPEC_REG "trap" , trapcmd },
10046 { BUILTIN_REGULAR "true" , truecmd },
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010047 { BUILTIN_REGULAR "type" , typecmd },
10048 { BUILTIN_REGULAR "ulimit" , ulimitcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010049 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010050#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010051 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010052#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010053 { BUILTIN_SPEC_REG "unset" , unsetcmd },
10054 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010055};
10056
Denis Vlasenko80591b02008-03-25 07:49:43 +000010057/* Should match the above table! */
10058#define COMMANDCMD (builtintab + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010059 /* . : */ 2 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010060 /* [ */ 1 * ENABLE_ASH_TEST + \
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010061 /* [[ */ 1 * BASH_TEST2 + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010062 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10063 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
10064 /* break cd cddir */ 3)
10065#define EVALCMD (COMMANDCMD + \
10066 /* command */ 1 * ENABLE_ASH_CMDCMD + \
10067 /* continue */ 1 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010068 /* echo */ 1 * ENABLE_ASH_ECHO + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010069 0)
10070#define EXECCMD (EVALCMD + \
10071 /* eval */ 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010072
10073/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010074 * Search the table of builtin commands.
10075 */
Denys Vlasenko888527c2016-10-02 16:54:17 +020010076static int
10077pstrcmp1(const void *a, const void *b)
10078{
10079 return strcmp((char*)a, *(char**)b + 1);
10080}
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010081static struct builtincmd *
10082find_builtin(const char *name)
10083{
10084 struct builtincmd *bp;
10085
10086 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +000010087 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denys Vlasenko888527c2016-10-02 16:54:17 +020010088 pstrcmp1
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010089 );
10090 return bp;
10091}
10092
Ron Yorston9e2a5662020-01-21 16:01:58 +000010093#if EDITING_HAS_get_exe_name
10094static const char * FAST_FUNC
10095get_builtin_name(int i)
10096{
10097 return /*i >= 0 &&*/ i < ARRAY_SIZE(builtintab) ? builtintab[i].name + 1 : NULL;
10098}
10099#endif
10100
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010101/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010102 * Execute a simple command.
10103 */
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010104static void unwindfiles(struct parsefile *stop);
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010105static int
10106isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +000010107{
10108 const char *q = endofname(p);
10109 if (p == q)
10110 return 0;
10111 return *q == '=';
10112}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010113static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010114bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010115{
10116 /* Preserve exitstatus of a previous possible redirection
10117 * as POSIX mandates */
10118 return back_exitstatus;
10119}
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010120static int
Eric Andersenc470f442003-07-28 09:56:35 +000010121evalcommand(union node *cmd, int flags)
10122{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010123 static const struct builtincmd null_bltin = {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010124 BUILTIN_REGULAR "", bltincmd
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010125 };
Denys Vlasenko484fc202017-07-26 19:55:31 +020010126 struct localvar_list *localvar_stop;
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010127 struct parsefile *file_stop;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010128 struct redirtab *redir_stop;
Eric Andersenc470f442003-07-28 09:56:35 +000010129 union node *argp;
10130 struct arglist arglist;
10131 struct arglist varlist;
10132 char **argv;
10133 int argc;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010134 struct strlist *osp;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010135 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +000010136 struct cmdentry cmdentry;
10137 struct job *jp;
10138 char *lastarg;
10139 const char *path;
10140 int spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010141 int cmd_flag;
Eric Andersenc470f442003-07-28 09:56:35 +000010142 int status;
10143 char **nargv;
Denis Vlasenko34c73c42008-08-16 11:48:02 +000010144 smallint cmd_is_exec;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010145 int vflags;
10146 int vlocal;
Eric Andersenc470f442003-07-28 09:56:35 +000010147
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010148 errlinno = lineno = cmd->ncmd.linno;
10149 if (funcline)
10150 lineno -= funcline - 1;
10151
Eric Andersenc470f442003-07-28 09:56:35 +000010152 /* First expand the arguments. */
10153 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010154 file_stop = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000010155 back_exitstatus = 0;
10156
10157 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010158 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +000010159 varlist.lastp = &varlist.list;
10160 *varlist.lastp = NULL;
10161 arglist.lastp = &arglist.list;
10162 *arglist.lastp = NULL;
10163
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010164 cmd_flag = 0;
10165 cmd_is_exec = 0;
10166 spclbltin = -1;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010167 vflags = 0;
10168 vlocal = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010169 path = NULL;
10170
Eric Andersenc470f442003-07-28 09:56:35 +000010171 argc = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010172 argp = cmd->ncmd.args;
10173 osp = fill_arglist(&arglist, &argp);
10174 if (osp) {
10175 int pseudovarflag = 0;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010176
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010177 for (;;) {
10178 find_command(arglist.list->text, &cmdentry,
10179 cmd_flag | DO_REGBLTIN, pathval());
Paul Foxc3850c82005-07-20 18:23:39 +000010180
Denys Vlasenko3e729102020-02-19 17:33:44 +010010181 vlocal++;
10182
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010183 /* implement bltin and command here */
10184 if (cmdentry.cmdtype != CMDBUILTIN)
10185 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010186
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010187 pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
10188 if (spclbltin < 0) {
10189 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
Denys Vlasenko3e729102020-02-19 17:33:44 +010010190 vlocal = !spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010191 }
10192 cmd_is_exec = cmdentry.u.cmd == EXECCMD;
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010193#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010194 if (cmdentry.u.cmd != COMMANDCMD)
10195 break;
Paul Foxc3850c82005-07-20 18:23:39 +000010196
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010197 cmd_flag = parse_command_args(&arglist, &argp, &path);
10198 if (!cmd_flag)
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010199#endif
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010200 break;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010201 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010202
10203 for (; argp; argp = argp->narg.next)
10204 expandarg(argp, &arglist,
10205 pseudovarflag &&
10206 isassignment(argp->narg.text) ?
10207 EXP_VARTILDE : EXP_FULL | EXP_TILDE);
10208
10209 for (sp = arglist.list; sp; sp = sp->next)
10210 argc++;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010211
10212 if (cmd_is_exec && argc > 1)
10213 vflags = VEXPORT;
Eric Andersenc470f442003-07-28 09:56:35 +000010214 }
10215
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010216 localvar_stop = pushlocalvars(vlocal);
10217
Denys Vlasenko65a8b852016-10-26 22:29:11 +020010218 /* Reserve one extra spot at the front for shellexec. */
10219 nargv = stalloc(sizeof(char *) * (argc + 2));
10220 argv = ++nargv;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010221 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +000010222 TRACE(("evalcommand arg: %s\n", sp->text));
10223 *nargv++ = sp->text;
10224 }
10225 *nargv = NULL;
10226
10227 lastarg = NULL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010228 if (iflag && funcline == 0 && argc > 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010229 lastarg = nargv[-1];
10230
10231 expredir(cmd->ncmd.redirect);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010232 redir_stop = pushredir(cmd->ncmd.redirect);
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010233 preverrout_fd = 2;
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +020010234 if (BASH_XTRACEFD && xflag) {
10235 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed.
10236 * we do not emulate this. We only use its value.
10237 */
10238 const char *xtracefd = lookupvar("BASH_XTRACEFD");
10239 if (xtracefd && is_number(xtracefd))
10240 preverrout_fd = atoi(xtracefd);
10241
10242 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010243 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +000010244
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010245 if (status) {
10246 bail:
10247 exitstatus = status;
10248
10249 /* We have a redirection error. */
10250 if (spclbltin > 0)
10251 raise_exception(EXERROR);
10252
10253 goto out;
10254 }
10255
Eric Andersenc470f442003-07-28 09:56:35 +000010256 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10257 struct strlist **spp;
Eric Andersenc470f442003-07-28 09:56:35 +000010258
10259 spp = varlist.lastp;
10260 expandarg(argp, &varlist, EXP_VARTILDE);
10261
Denys Vlasenko3e729102020-02-19 17:33:44 +010010262 if (vlocal)
10263 mklocal((*spp)->text, VEXPORT);
10264 else
10265 setvareq((*spp)->text, vflags);
Eric Andersenc470f442003-07-28 09:56:35 +000010266 }
10267
10268 /* Print the command if xflag is set. */
10269 if (xflag) {
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010270 const char *pfx = "";
Eric Andersenc470f442003-07-28 09:56:35 +000010271
Denys Vlasenko46999802017-07-29 21:12:29 +020010272 fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX));
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010273
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010274 sp = varlist.list;
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010275 while (sp) {
10276 char *varval = sp->text;
10277 char *eq = strchrnul(varval, '=');
10278 if (*eq)
10279 eq++;
10280 fdprintf(preverrout_fd, "%s%.*s%s",
10281 pfx,
10282 (int)(eq - varval), varval,
10283 maybe_single_quote(eq)
10284 );
10285 sp = sp->next;
10286 pfx = " ";
10287 }
10288
10289 sp = arglist.list;
10290 while (sp) {
10291 fdprintf(preverrout_fd, "%s%s",
10292 pfx,
10293 /* always quote if matches reserved word: */
10294 findkwd(sp->text)
10295 ? single_quote(sp->text)
10296 : maybe_single_quote(sp->text)
10297 );
10298 sp = sp->next;
10299 pfx = " ";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010300 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010301 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010302 }
10303
Eric Andersenc470f442003-07-28 09:56:35 +000010304 /* Now locate the command. */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010305 if (cmdentry.cmdtype != CMDBUILTIN
10306 || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
10307 ) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010308 path = path ? path : pathval();
10309 find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path);
Eric Andersenc470f442003-07-28 09:56:35 +000010310 }
10311
Denys Vlasenkod81af722020-02-18 14:28:30 +010010312 jp = NULL;
10313
Eric Andersenc470f442003-07-28 09:56:35 +000010314 /* Execute the command. */
10315 switch (cmdentry.cmdtype) {
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010316 case CMDUNKNOWN:
10317 status = 127;
10318 flush_stdout_stderr();
10319 goto bail;
10320
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010321 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +000010322
Denys Vlasenko1750d3a2018-01-15 00:41:04 +010010323#if ENABLE_FEATURE_SH_STANDALONE \
10324 && ENABLE_FEATURE_SH_NOFORK \
10325 && NUM_APPLETS > 1
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010326/* (1) BUG: if variables are set, we need to fork, or save/restore them
10327 * around run_nofork_applet() call.
10328 * (2) Should this check also be done in forkshell()?
10329 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
10330 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000010331 /* find_command() encodes applet_no as (-2 - applet_no) */
10332 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010333 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010334 char **sv_environ;
10335
10336 INT_OFF;
10337 sv_environ = environ;
10338 environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL);
Denys Vlasenkod329e342017-08-04 14:50:03 +020010339 /*
10340 * Run <applet>_main().
10341 * Signals (^C) can't interrupt here.
10342 * Otherwise we can mangle stdio or malloc internal state.
10343 * This makes applets which can run for a long time
10344 * and/or wait for user input ineligible for NOFORK:
10345 * for example, "yes" or "rm" (rm -i waits for input).
10346 */
Ron Yorstond5bfe262020-02-20 08:23:03 +000010347 exitstatus = run_nofork_applet(applet_no, argv);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010348 environ = sv_environ;
Denys Vlasenkod329e342017-08-04 14:50:03 +020010349 /*
10350 * Try enabling NOFORK for "yes" applet.
10351 * ^C _will_ stop it (write returns EINTR),
10352 * but this causes stdout FILE to be stuck
10353 * and needing clearerr(). What if other applets
10354 * also can get EINTRs? Do we need to switch
10355 * our signals to SA_RESTART?
10356 */
10357 /*clearerr(stdout);*/
10358 INT_ON;
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010359 break;
10360 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010361#endif
Denys Vlasenkocfd392b2017-08-03 19:56:29 +020010362 /* Can we avoid forking? For example, very last command
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010363 * in a script or a subshell does not need forking,
10364 * we can just exec it.
10365 */
Denys Vlasenko238bf182010-05-18 15:49:07 +020010366 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010367 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010368 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +010010369 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +000010370 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010371 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +020010372 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +000010373 break;
10374 }
Denys Vlasenko238bf182010-05-18 15:49:07 +020010375 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010376 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +020010377 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +000010378 }
Denys Vlasenkoe139ae32017-04-12 21:02:33 +020010379 shellexec(argv[0], argv, path, cmdentry.u.index);
Eric Andersenc470f442003-07-28 09:56:35 +000010380 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010381 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +000010382 case CMDBUILTIN:
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010383 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)
10384 && !(exception_type == EXERROR && spclbltin <= 0)
10385 ) {
Denys Vlasenkoc0663c72016-10-27 21:09:01 +020010386 raise:
10387 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010388 }
Denys Vlasenkod81af722020-02-18 14:28:30 +010010389 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010390
10391 case CMDFUNCTION:
Eric Andersenc470f442003-07-28 09:56:35 +000010392 if (evalfun(cmdentry.u.func, argc, argv, flags))
10393 goto raise;
10394 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010395 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +000010396
Denys Vlasenkod81af722020-02-18 14:28:30 +010010397 status = waitforjob(jp);
Ron Yorston6cda0b02020-02-21 16:16:56 +000010398 if (jp)
10399 TRACE(("forked child exited with %d\n", status));
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010400 FORCE_INT_ON;
Denys Vlasenkod81af722020-02-18 14:28:30 +010010401
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010402 out:
Denys Vlasenkoeaf94362016-10-25 21:46:03 +020010403 if (cmd->ncmd.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +020010404 popredir(/*drop:*/ cmd_is_exec);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010405 unwindredir(redir_stop);
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010406 unwindfiles(file_stop);
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010407 unwindlocalvars(localvar_stop);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010408 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +000010409 /* dsl: I think this is intended to be used to support
10410 * '_' in 'vi' command mode during line editing...
10411 * However I implemented that within libedit itself.
10412 */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010413 setvar0("_", lastarg);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010414 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010415
10416 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000010417}
10418
10419static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010420evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags)
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010421{
Eric Andersenc470f442003-07-28 09:56:35 +000010422 char *volatile savecmdname;
10423 struct jmploc *volatile savehandler;
10424 struct jmploc jmploc;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010425 int status;
Eric Andersenc470f442003-07-28 09:56:35 +000010426 int i;
10427
10428 savecmdname = commandname;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +020010429 savehandler = exception_handler;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010430 i = setjmp(jmploc.loc);
10431 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +000010432 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010433 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +000010434 commandname = argv[0];
10435 argptr = argv + 1;
10436 optptr = NULL; /* initialize nextopt */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010437 if (cmd == EVALCMD)
10438 status = evalcmd(argc, argv, flags);
10439 else
10440 status = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010441 flush_stdout_stderr();
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010442 status |= ferror(stdout);
10443 exitstatus = status;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010444 cmddone:
Rob Landleyf296f0b2006-07-06 01:09:21 +000010445 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000010446 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010447 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010448
10449 return i;
10450}
10451
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010452static int
10453goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010454{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +020010455 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010456}
10457
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010458
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010459/*
10460 * Search for a command. This is called before we fork so that the
10461 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010462 * the child. The check for "goodname" is an overly conservative
10463 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010464 */
Eric Andersenc470f442003-07-28 09:56:35 +000010465static void
10466prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010467{
10468 struct cmdentry entry;
10469
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010470 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
10471 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010472}
10473
Eric Andersencb57d552001-06-28 07:25:16 +000010474
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010475/* ============ Builtin commands
10476 *
10477 * Builtin commands whose functions are closely tied to evaluation
10478 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +000010479 */
10480
10481/*
Eric Andersencb57d552001-06-28 07:25:16 +000010482 * Handle break and continue commands. Break, continue, and return are
10483 * all handled by setting the evalskip flag. The evaluation routines
10484 * above all check this flag, and if it is set they start skipping
10485 * commands rather than executing them. The variable skipcount is
10486 * the number of loops to break/continue, or the number of function
10487 * levels to return. (The latter is always 1.) It should probably
10488 * be an error to break out of more loops than exist, but it isn't
10489 * in the standard shell so we don't make it one here.
10490 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010491static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010492breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010493{
Denis Vlasenko68404f12008-03-17 09:00:54 +000010494 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010495
Aaron Lehmann2aef3a62001-12-31 06:03:12 +000010496 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020010497 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +000010498 if (n > loopnest)
10499 n = loopnest;
10500 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010501 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +000010502 skipcount = n;
10503 }
10504 return 0;
10505}
10506
Eric Andersenc470f442003-07-28 09:56:35 +000010507
Denys Vlasenko70392332016-10-27 02:31:55 +020010508/*
Eric Andersen90898442003-08-06 11:20:52 +000010509 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +000010510 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010511
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010512enum {
10513 INPUT_PUSH_FILE = 1,
10514 INPUT_NOFILE_OK = 2,
10515};
Eric Andersencb57d552001-06-28 07:25:16 +000010516
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010517static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010518/* values of checkkwd variable */
10519#define CHKALIAS 0x1
10520#define CHKKWD 0x2
10521#define CHKNL 0x4
Denys Vlasenkoa7328982017-07-29 19:57:28 +020010522#define CHKEOFMARK 0x8
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010523
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010524/*
10525 * Push a string back onto the input at this current parsefile level.
10526 * We handle aliases this way.
10527 */
10528#if !ENABLE_ASH_ALIAS
10529#define pushstring(s, ap) pushstring(s)
10530#endif
10531static void
10532pushstring(char *s, struct alias *ap)
10533{
10534 struct strpush *sp;
10535 int len;
10536
10537 len = strlen(s);
10538 INT_OFF;
10539 if (g_parsefile->strpush) {
10540 sp = ckzalloc(sizeof(*sp));
10541 sp->prev = g_parsefile->strpush;
10542 } else {
10543 sp = &(g_parsefile->basestrpush);
10544 }
10545 g_parsefile->strpush = sp;
10546 sp->prev_string = g_parsefile->next_to_pgetc;
10547 sp->prev_left_in_line = g_parsefile->left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010548 sp->unget = g_parsefile->unget;
10549 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010550#if ENABLE_ASH_ALIAS
10551 sp->ap = ap;
10552 if (ap) {
10553 ap->flag |= ALIASINUSE;
10554 sp->string = s;
10555 }
10556#endif
10557 g_parsefile->next_to_pgetc = s;
10558 g_parsefile->left_in_line = len;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010559 g_parsefile->unget = 0;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010560 INT_ON;
10561}
10562
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010563static void
10564popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +000010565{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010566 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +000010567
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010568 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010569#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010570 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010571 if (g_parsefile->next_to_pgetc[-1] == ' '
10572 || g_parsefile->next_to_pgetc[-1] == '\t'
10573 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010574 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010575 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010576 if (sp->string != sp->ap->val) {
10577 free(sp->string);
10578 }
10579 sp->ap->flag &= ~ALIASINUSE;
10580 if (sp->ap->flag & ALIASDEAD) {
10581 unalias(sp->ap->name);
10582 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010583 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010584#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010585 g_parsefile->next_to_pgetc = sp->prev_string;
10586 g_parsefile->left_in_line = sp->prev_left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010587 g_parsefile->unget = sp->unget;
10588 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010589 g_parsefile->strpush = sp->prev;
10590 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010591 free(sp);
10592 INT_ON;
10593}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010594
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010595static int
10596preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010597{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010598 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +000010599 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010600
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010601 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +000010602#if ENABLE_FEATURE_EDITING
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010603 /* retry: */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010604 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Ron Yorston61d6ae22015-04-19 10:50:25 +010010605 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010606 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010607# if ENABLE_ASH_IDLE_TIMEOUT
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010608 int timeout = -1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020010609 const char *tmout_var = lookupvar("TMOUT");
10610 if (tmout_var) {
10611 timeout = atoi(tmout_var) * 1000;
10612 if (timeout <= 0)
10613 timeout = -1;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010614 }
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010615 line_input_state->timeout = timeout;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010616# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010617# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010618 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010619# endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +020010620 reinit_unicode_for_ash();
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010621 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010622 if (nr == 0) {
Denys Vlasenko4b89d512016-11-25 03:41:03 +010010623 /* ^C pressed, "convert" to SIGINT */
10624 write(STDOUT_FILENO, "^C", 2);
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010625 raise(SIGINT);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010626 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010627 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010628 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010629 return 1;
10630 }
Denys Vlasenko8660aeb2016-11-24 17:44:02 +010010631 exitstatus = 128 + SIGINT;
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010632 return -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010633 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010634 if (nr < 0) {
10635 if (errno == 0) {
10636 /* Ctrl+D pressed */
10637 nr = 0;
10638 }
10639# if ENABLE_ASH_IDLE_TIMEOUT
10640 else if (errno == EAGAIN && timeout > 0) {
Denys Vlasenkod60752f2015-10-07 22:42:45 +020010641 puts("\007timed out waiting for input: auto-logout");
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010642 exitshell();
10643 }
10644# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010645 }
Eric Andersencb57d552001-06-28 07:25:16 +000010646 }
10647#else
Ron Yorston61d6ae22015-04-19 10:50:25 +010010648 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +000010649#endif
10650
Denys Vlasenko80c5b682011-05-08 21:21:10 +020010651#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +000010652 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010653 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +000010654 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +000010655 if (flags >= 0 && (flags & O_NONBLOCK)) {
10656 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +000010657 if (fcntl(0, F_SETFL, flags) >= 0) {
10658 out2str("sh: turning off NDELAY mode\n");
10659 goto retry;
10660 }
10661 }
10662 }
10663 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +000010664#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010665 return nr;
10666}
10667
10668/*
10669 * Refill the input buffer and return the next input character:
10670 *
10671 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010672 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
10673 * or we are reading from a string so we can't refill the buffer,
10674 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +020010675 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +000010676 * 4) Process input up to the next newline, deleting nul characters.
10677 */
Denis Vlasenko727752d2008-11-28 03:41:47 +000010678//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
10679#define pgetc_debug(...) ((void)0)
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010680static int pgetc(void);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010681static int
Eric Andersenc470f442003-07-28 09:56:35 +000010682preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010683{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010684 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010685 int more;
Eric Andersencb57d552001-06-28 07:25:16 +000010686
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010687 if (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010688#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010689 if (g_parsefile->left_in_line == -1
10690 && g_parsefile->strpush->ap
10691 && g_parsefile->next_to_pgetc[-1] != ' '
10692 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +000010693 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010694 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +000010695 return PEOA;
10696 }
Eric Andersen2870d962001-07-02 17:27:21 +000010697#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010698 popstring();
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010699 return pgetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010700 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010701 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010702 * "pgetc" needs refilling.
10703 */
10704
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +000010705 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010706 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +000010707 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010708 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010709 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010710 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010711 /* even in failure keep left_in_line and next_to_pgetc
10712 * in lock step, for correct multi-layer pungetc.
10713 * left_in_line was decremented before preadbuffer(),
10714 * must inc next_to_pgetc: */
10715 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010716 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010717 }
Eric Andersencb57d552001-06-28 07:25:16 +000010718
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010719 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010720 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010721 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010722 again:
10723 more = preadfd();
10724 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010725 /* don't try reading again */
10726 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010727 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010728 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010729 return PEOF;
10730 }
10731 }
10732
Denis Vlasenko727752d2008-11-28 03:41:47 +000010733 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010734 * Set g_parsefile->left_in_line
10735 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010736 * NUL chars are deleted.
10737 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010738 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010739 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010740 char c;
Eric Andersencb57d552001-06-28 07:25:16 +000010741
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010742 more--;
Eric Andersenc470f442003-07-28 09:56:35 +000010743
Denis Vlasenko727752d2008-11-28 03:41:47 +000010744 c = *q;
10745 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010746 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010747 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010748 q++;
10749 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010750 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010751 break;
10752 }
Eric Andersencb57d552001-06-28 07:25:16 +000010753 }
10754
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010755 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010756 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
10757 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010758 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010759 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010760 }
10761 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010762 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +000010763
Eric Andersencb57d552001-06-28 07:25:16 +000010764 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010765 char save = *q;
10766 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010767 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010768 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +000010769 }
10770
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010771 pgetc_debug("preadbuffer at %d:%p'%s'",
10772 g_parsefile->left_in_line,
10773 g_parsefile->next_to_pgetc,
10774 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +010010775 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010776}
10777
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010778static void
10779nlprompt(void)
10780{
10781 g_parsefile->linno++;
10782 setprompt_if(doprompt, 2);
10783}
10784static void
10785nlnoprompt(void)
10786{
10787 g_parsefile->linno++;
10788 needprompt = doprompt;
10789}
10790
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010791static int
10792pgetc(void)
10793{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010794 int c;
10795
10796 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010797 g_parsefile->left_in_line,
10798 g_parsefile->next_to_pgetc,
10799 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010800 if (g_parsefile->unget)
10801 return g_parsefile->lastc[--g_parsefile->unget];
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010802
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010803 if (--g_parsefile->left_in_line >= 0)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +010010804 c = (unsigned char)*g_parsefile->next_to_pgetc++;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010805 else
10806 c = preadbuffer();
10807
10808 g_parsefile->lastc[1] = g_parsefile->lastc[0];
10809 g_parsefile->lastc[0] = c;
10810
10811 return c;
10812}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010813
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010814#if ENABLE_ASH_ALIAS
10815static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010010816pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010817{
10818 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010819 do {
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010820 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010821 g_parsefile->left_in_line,
10822 g_parsefile->next_to_pgetc,
10823 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010824 c = pgetc();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010825 } while (c == PEOA);
10826 return c;
10827}
10828#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010010829# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010830#endif
10831
10832/*
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010833 * Undo a call to pgetc. Only two characters may be pushed back.
Eric Andersenc470f442003-07-28 09:56:35 +000010834 * PEOF may be pushed back.
10835 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010836static void
Eric Andersenc470f442003-07-28 09:56:35 +000010837pungetc(void)
10838{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010839 g_parsefile->unget++;
Eric Andersencb57d552001-06-28 07:25:16 +000010840}
10841
Denys Vlasenko73c3e072016-09-29 17:17:04 +020010842/* This one eats backslash+newline */
10843static int
10844pgetc_eatbnl(void)
10845{
10846 int c;
10847
10848 while ((c = pgetc()) == '\\') {
10849 if (pgetc() != '\n') {
10850 pungetc();
10851 break;
10852 }
10853
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010854 nlprompt();
Denys Vlasenko73c3e072016-09-29 17:17:04 +020010855 }
10856
10857 return c;
10858}
10859
Denys Vlasenko216913c2018-04-02 12:35:04 +020010860struct synstack {
10861 smalluint syntax;
10862 uint8_t innerdq :1;
10863 uint8_t varpushed :1;
10864 uint8_t dblquote :1;
10865 int varnest; /* levels of variables expansion */
10866 int dqvarnest; /* levels of variables expansion within double quotes */
10867 int parenlevel; /* levels of parens in arithmetic */
10868 struct synstack *prev;
10869 struct synstack *next;
10870};
10871
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010010872static int
10873pgetc_top(struct synstack *stack)
10874{
10875 return stack->syntax == SQSYNTAX ? pgetc() : pgetc_eatbnl();
10876}
10877
Denys Vlasenko216913c2018-04-02 12:35:04 +020010878static void
10879synstack_push(struct synstack **stack, struct synstack *next, int syntax)
10880{
10881 memset(next, 0, sizeof(*next));
10882 next->syntax = syntax;
10883 next->next = *stack;
10884 (*stack)->prev = next;
10885 *stack = next;
10886}
10887
10888static ALWAYS_INLINE void
10889synstack_pop(struct synstack **stack)
10890{
10891 *stack = (*stack)->next;
10892}
10893
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010894/*
10895 * To handle the "." command, a stack of input files is used. Pushfile
10896 * adds a new entry to the stack and popfile restores the previous level.
10897 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010898static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010899pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +000010900{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010901 struct parsefile *pf;
10902
Denis Vlasenko597906c2008-02-20 16:38:54 +000010903 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010904 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010905 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010906 /*pf->strpush = NULL; - ckzalloc did it */
10907 /*pf->basestrpush.prev = NULL;*/
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010908 /*pf->unget = 0;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010909 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010910}
10911
10912static void
10913popfile(void)
10914{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010915 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000010916
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010010917 if (pf == &basepf)
10918 return;
10919
Denis Vlasenkob012b102007-02-19 22:43:01 +000010920 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010921 if (pf->pf_fd >= 0)
10922 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +000010923 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010924 while (pf->strpush)
10925 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010926 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010927 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010928 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010929}
10930
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010931static void
10932unwindfiles(struct parsefile *stop)
10933{
10934 while (g_parsefile != stop)
10935 popfile();
10936}
10937
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010938/*
10939 * Return to top level.
10940 */
10941static void
10942popallfiles(void)
10943{
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010944 unwindfiles(&basepf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010945}
10946
10947/*
10948 * Close the file(s) that the shell is reading commands from. Called
10949 * after a fork is done.
10950 */
10951static void
10952closescript(void)
10953{
10954 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010955 if (g_parsefile->pf_fd > 0) {
10956 close(g_parsefile->pf_fd);
10957 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010958 }
10959}
10960
10961/*
10962 * Like setinputfile, but takes an open file descriptor. Call this with
10963 * interrupts off.
10964 */
10965static void
10966setinputfd(int fd, int push)
10967{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010968 if (push) {
10969 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +000010970 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010971 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010972 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010973 if (g_parsefile->buf == NULL)
10974 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010975 g_parsefile->left_in_buffer = 0;
10976 g_parsefile->left_in_line = 0;
10977 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010978}
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010979
Eric Andersenc470f442003-07-28 09:56:35 +000010980/*
10981 * Set the input to take input from a file. If push is set, push the
10982 * old input onto the stack first.
10983 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010984static int
10985setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000010986{
10987 int fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010988
Denis Vlasenkob012b102007-02-19 22:43:01 +000010989 INT_OFF;
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020010990 fd = open(fname, O_RDONLY | O_CLOEXEC);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010991 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010992 if (flags & INPUT_NOFILE_OK)
10993 goto out;
Denys Vlasenkob7adf7a2016-10-25 17:00:13 +020010994 exitstatus = 127;
Johannes Schindelin20a63b22017-08-22 22:03:17 +020010995 ash_msg_and_raise_perror("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010996 }
Denys Vlasenko64774602016-10-26 15:24:30 +020010997 if (fd < 10)
10998 fd = savefd(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020010999 else if (O_CLOEXEC == 0) /* old libc */
Denys Vlasenkoe19923f2016-10-26 15:38:44 +020011000 close_on_exec_on(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011001
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011002 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011003 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +000011004 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011005 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011006}
11007
Eric Andersencb57d552001-06-28 07:25:16 +000011008/*
11009 * Like setinputfile, but takes input from a string.
11010 */
Eric Andersenc470f442003-07-28 09:56:35 +000011011static void
11012setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000011013{
Denis Vlasenkob012b102007-02-19 22:43:01 +000011014 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011015 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011016 g_parsefile->next_to_pgetc = string;
11017 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011018 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011019 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011020 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011021}
11022
11023
Denys Vlasenko70392332016-10-27 02:31:55 +020011024/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011025 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000011026 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011027
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011028#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000011029
Denys Vlasenko23841622015-10-09 15:52:03 +020011030/* Hash of mtimes of mailboxes */
11031static unsigned mailtime_hash;
Eric Andersenc470f442003-07-28 09:56:35 +000011032/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011033static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000011034
Eric Andersencb57d552001-06-28 07:25:16 +000011035/*
Eric Andersenc470f442003-07-28 09:56:35 +000011036 * Print appropriate message(s) if mail has arrived.
11037 * If mail_var_path_changed is set,
11038 * then the value of MAIL has mail_var_path_changed,
11039 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000011040 */
Eric Andersenc470f442003-07-28 09:56:35 +000011041static void
11042chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000011043{
Eric Andersencb57d552001-06-28 07:25:16 +000011044 const char *mpath;
11045 char *p;
11046 char *q;
Denys Vlasenko23841622015-10-09 15:52:03 +020011047 unsigned new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011048 struct stackmark smark;
11049 struct stat statb;
11050
Eric Andersencb57d552001-06-28 07:25:16 +000011051 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000011052 mpath = mpathset() ? mpathval() : mailval();
Denys Vlasenko23841622015-10-09 15:52:03 +020011053 new_hash = 0;
11054 for (;;) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011055 int len;
11056
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010011057 len = padvance_magic(&mpath, nullstr, 2);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011058 if (!len)
11059 break;
11060 p = stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011061 break;
11062 if (*p == '\0')
11063 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011064 for (q = p; *q; q++)
11065 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011066#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011067 if (q[-1] != '/')
11068 abort();
11069#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011070 q[-1] = '\0'; /* delete trailing '/' */
11071 if (stat(p, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011072 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000011073 }
Denys Vlasenko23841622015-10-09 15:52:03 +020011074 /* Very simplistic "hash": just a sum of all mtimes */
11075 new_hash += (unsigned)statb.st_mtime;
11076 }
11077 if (!mail_var_path_changed && mailtime_hash != new_hash) {
Denys Vlasenko4cd99e72015-10-09 16:02:53 +020011078 if (mailtime_hash != 0)
11079 out2str("you have mail\n");
Denys Vlasenko23841622015-10-09 15:52:03 +020011080 mailtime_hash = new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011081 }
Eric Andersenc470f442003-07-28 09:56:35 +000011082 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011083 popstackmark(&smark);
11084}
Eric Andersencb57d552001-06-28 07:25:16 +000011085
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011086static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011087changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011088{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011089 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011090}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011091
Denis Vlasenko131ae172007-02-18 13:00:19 +000011092#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000011093
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011094
11095/* ============ ??? */
11096
Eric Andersencb57d552001-06-28 07:25:16 +000011097/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011098 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000011099 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011100static void
11101setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011102{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011103 char **newparam;
11104 char **ap;
11105 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000011106
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011107 for (nparam = 0; argv[nparam]; nparam++)
11108 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011109 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
11110 while (*argv) {
11111 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000011112 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011113 *ap = NULL;
11114 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000011115 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011116 shellparam.nparam = nparam;
11117 shellparam.p = newparam;
11118#if ENABLE_ASH_GETOPTS
11119 shellparam.optind = 1;
11120 shellparam.optoff = -1;
11121#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011122}
11123
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011124/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011125 * Process shell options. The global variable argptr contains a pointer
11126 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011127 *
11128 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
11129 * For a non-interactive shell, an error condition encountered
11130 * by a special built-in ... shall cause the shell to write a diagnostic message
11131 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000011132 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011133 * ...
11134 * Utility syntax error (option or operand error) Shall exit
11135 * ...
11136 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
11137 * we see that bash does not do that (set "finishes" with error code 1 instead,
11138 * and shell continues), and people rely on this behavior!
11139 * Testcase:
11140 * set -o barfoo 2>/dev/null
11141 * echo $?
11142 *
11143 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011144 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011145static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011146plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000011147{
11148 int i;
11149
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011150 if (name) {
11151 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011152 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011153 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011154 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011155 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011156 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011157 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011158 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000011159 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011160 for (i = 0; i < NOPTS; i++) {
Denys Vlasenko2f9c1242019-08-02 16:43:36 +020011161 if (optnames(i)[0] == '\0')
11162 continue;
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011163 if (val) {
11164 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
11165 } else {
11166 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
11167 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011168 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011169 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011170}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011171static void
11172setoption(int flag, int val)
11173{
11174 int i;
11175
11176 for (i = 0; i < NOPTS; i++) {
Denys Vlasenkof3634582019-06-03 12:21:04 +020011177 if (optletters(i) == flag && optnames(i)[0] != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011178 optlist[i] = val;
11179 return;
11180 }
11181 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011182 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011183 /* NOTREACHED */
11184}
Denys Vlasenko897475a2019-06-01 16:35:09 +020011185/* If login_sh is not NULL, we are called to parse command line opts,
11186 * not "set -opts"
11187 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011188static int
Denys Vlasenko897475a2019-06-01 16:35:09 +020011189options(int *login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011190{
11191 char *p;
11192 int val;
11193 int c;
11194
Denys Vlasenko897475a2019-06-01 16:35:09 +020011195 if (login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011196 minusc = NULL;
11197 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011198 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011199 if (c != '-' && c != '+')
11200 break;
11201 argptr++;
11202 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011203 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000011204 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011205 if (p[0] == '\0' || LONE_DASH(p)) {
Denys Vlasenko897475a2019-06-01 16:35:09 +020011206 if (!login_sh) {
Eric Andersen2870d962001-07-02 17:27:21 +000011207 /* "-" means turn off -x and -v */
11208 if (p[0] == '\0')
11209 xflag = vflag = 0;
11210 /* "--" means reset params */
11211 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011212 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000011213 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011214 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000011215 }
Eric Andersencb57d552001-06-28 07:25:16 +000011216 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011217 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000011218 while ((c = *p++) != '\0') {
Denys Vlasenko897475a2019-06-01 16:35:09 +020011219 if (login_sh) {
11220 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
11221 if (c == 'c') {
11222 minusc = p; /* command is after shell args */
Denys Vlasenkof3634582019-06-03 12:21:04 +020011223 cflag = 1;
11224 continue;
11225 }
11226 if (c == 's') { /* -s, +s */
11227 sflag = 1;
11228 continue;
11229 }
11230 if (c == 'i') { /* -i, +i */
11231 iflag = 1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011232 continue;
11233 }
11234 if (c == 'l') {
11235 *login_sh = 1; /* -l or +l == --login */
11236 continue;
11237 }
11238 /* bash does not accept +-login, we also won't */
11239 if (val && (c == '-')) { /* long options */
11240 if (strcmp(p, "login") == 0) {
11241 *login_sh = 1;
11242 }
11243 break;
11244 }
11245 }
11246 if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011247 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011248 /* it already printed err message */
11249 return 1; /* error */
11250 }
Eric Andersencb57d552001-06-28 07:25:16 +000011251 if (*argptr)
11252 argptr++;
11253 } else {
11254 setoption(c, val);
11255 }
11256 }
11257 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011258 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011259}
11260
Eric Andersencb57d552001-06-28 07:25:16 +000011261/*
Eric Andersencb57d552001-06-28 07:25:16 +000011262 * The shift builtin command.
11263 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011264static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011265shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011266{
11267 int n;
11268 char **ap1, **ap2;
11269
11270 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011271 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011272 n = number(argv[1]);
11273 if (n > shellparam.nparam)
Ingo van Lil9c8e94b2018-01-05 15:04:23 +010011274 return 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011275 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011276 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011277 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000011278 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011279 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000011280 }
11281 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011282 while ((*ap2++ = *ap1++) != NULL)
11283 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011284#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011285 shellparam.optind = 1;
11286 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000011287#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000011288 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011289 return 0;
11290}
11291
Eric Andersencb57d552001-06-28 07:25:16 +000011292/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011293 * POSIX requires that 'set' (but not export or readonly) output the
11294 * variables in lexicographic order - by the locale's collating order (sigh).
11295 * Maybe we could keep them in an ordered balanced binary tree
11296 * instead of hashed lists.
11297 * For now just roll 'em through qsort for printing...
11298 */
11299static int
11300showvars(const char *sep_prefix, int on, int off)
11301{
11302 const char *sep;
11303 char **ep, **epend;
11304
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010011305 ep = listvars(on, off, /*strlist:*/ NULL, &epend);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011306 qsort(ep, epend - ep, sizeof(char *), vpcmp);
11307
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011308 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011309
11310 for (; ep < epend; ep++) {
11311 const char *p;
11312 const char *q;
11313
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011314 p = endofname(*ep);
11315/* Used to have simple "p = strchrnul(*ep, '=')" here instead, but this
11316 * makes "export -p" to have output not suitable for "eval":
11317 * import os
11318 * os.environ["test-test"]="test"
11319 * if os.fork() == 0:
11320 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
11321 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ])
11322 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011323 q = nullstr;
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011324 if (*p == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011325 q = single_quote(++p);
11326 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
11327 }
11328 return 0;
11329}
11330
11331/*
Eric Andersencb57d552001-06-28 07:25:16 +000011332 * The set command builtin.
11333 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011334static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011335setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011336{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011337 int retval;
11338
Denis Vlasenko68404f12008-03-17 09:00:54 +000011339 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000011340 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011341
Denis Vlasenkob012b102007-02-19 22:43:01 +000011342 INT_OFF;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011343 retval = options(/*login_sh:*/ NULL);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011344 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011345 optschanged();
11346 if (*argptr != NULL) {
11347 setparam(argptr);
11348 }
Eric Andersencb57d552001-06-28 07:25:16 +000011349 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011350 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011351 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000011352}
11353
Denis Vlasenko131ae172007-02-18 13:00:19 +000011354#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011355static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011356change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000011357{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011358 uint32_t t;
11359
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011360 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000011361 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011362 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000011363 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020011364 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000011365 vrandom.flags &= ~VNOFUNC;
11366 } else {
11367 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011368 t = strtoul(value, NULL, 10);
11369 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000011370 }
Eric Andersenef02f822004-03-11 13:34:24 +000011371}
Eric Andersen16767e22004-03-16 05:14:10 +000011372#endif
11373
Ron Yorston1d371862019-04-15 10:52:05 +010011374#if BASH_EPOCH_VARS
11375static void FAST_FUNC
11376change_epoch(struct var *vepoch, const char *fmt)
11377{
11378 struct timeval tv;
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011379 char buffer[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
Ron Yorston1d371862019-04-15 10:52:05 +010011380
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011381 xgettimeofday(&tv);
11382 sprintf(buffer, fmt, (unsigned long long)tv.tv_sec, (unsigned)tv.tv_usec);
Ron Yorston1d371862019-04-15 10:52:05 +010011383 setvar(vepoch->var_text, buffer, VNOFUNC);
11384 vepoch->flags &= ~VNOFUNC;
11385}
11386
11387static void FAST_FUNC
11388change_seconds(const char *value UNUSED_PARAM)
11389{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011390 change_epoch(&vepochs, "%llu");
Ron Yorston1d371862019-04-15 10:52:05 +010011391}
11392
11393static void FAST_FUNC
11394change_realtime(const char *value UNUSED_PARAM)
11395{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011396 change_epoch(&vepochr, "%llu.%06u");
Ron Yorston1d371862019-04-15 10:52:05 +010011397}
11398#endif
11399
Denis Vlasenko131ae172007-02-18 13:00:19 +000011400#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011401static int
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011402getopts(char *optstr, char *optvar, char **optfirst)
Eric Andersencb57d552001-06-28 07:25:16 +000011403{
11404 char *p, *q;
11405 char c = '?';
11406 int done = 0;
Denys Vlasenko9c541002015-10-07 15:44:36 +020011407 char sbuf[2];
Eric Andersena48b0a32003-10-22 10:56:47 +000011408 char **optnext;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011409 int ind = shellparam.optind;
11410 int off = shellparam.optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000011411
Denys Vlasenko9c541002015-10-07 15:44:36 +020011412 sbuf[1] = '\0';
11413
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011414 shellparam.optind = -1;
11415 optnext = optfirst + ind - 1;
Eric Andersena48b0a32003-10-22 10:56:47 +000011416
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011417 if (ind <= 1 || off < 0 || (int)strlen(optnext[-1]) < off)
Eric Andersencb57d552001-06-28 07:25:16 +000011418 p = NULL;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011419 else
11420 p = optnext[-1] + off;
Eric Andersencb57d552001-06-28 07:25:16 +000011421 if (p == NULL || *p == '\0') {
11422 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000011423 p = *optnext;
11424 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011425 atend:
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011426 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011427 p = NULL;
11428 done = 1;
11429 goto out;
11430 }
11431 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011432 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000011433 goto atend;
11434 }
11435
11436 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000011437 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000011438 if (*q == '\0') {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011439 /* OPTERR is a bashism */
11440 const char *cp = lookupvar("OPTERR");
11441 if ((cp && LONE_CHAR(cp, '0'))
11442 || (optstr[0] == ':')
11443 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011444 sbuf[0] = c;
11445 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011446 setvar0("OPTARG", sbuf);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011447 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011448 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011449 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011450 }
11451 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000011452 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011453 }
11454 if (*++q == ':')
11455 q++;
11456 }
11457
11458 if (*++q == ':') {
11459 if (*p == '\0' && (p = *optnext) == NULL) {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011460 /* OPTERR is a bashism */
11461 const char *cp = lookupvar("OPTERR");
11462 if ((cp && LONE_CHAR(cp, '0'))
11463 || (optstr[0] == ':')
11464 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011465 sbuf[0] = c;
11466 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011467 setvar0("OPTARG", sbuf);
Eric Andersencb57d552001-06-28 07:25:16 +000011468 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011469 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011470 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011471 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011472 c = '?';
11473 }
Eric Andersenc470f442003-07-28 09:56:35 +000011474 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011475 }
11476
11477 if (p == *optnext)
11478 optnext++;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011479 setvar0("OPTARG", p);
Eric Andersencb57d552001-06-28 07:25:16 +000011480 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011481 } else
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011482 setvar0("OPTARG", nullstr);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011483 out:
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011484 ind = optnext - optfirst + 1;
11485 setvar("OPTIND", itoa(ind), VNOFUNC);
Denys Vlasenko9c541002015-10-07 15:44:36 +020011486 sbuf[0] = c;
11487 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011488 setvar0(optvar, sbuf);
11489
11490 shellparam.optoff = p ? p - *(optnext - 1) : -1;
11491 shellparam.optind = ind;
11492
Eric Andersencb57d552001-06-28 07:25:16 +000011493 return done;
11494}
Eric Andersenc470f442003-07-28 09:56:35 +000011495
11496/*
11497 * The getopts builtin. Shellparam.optnext points to the next argument
11498 * to be processed. Shellparam.optptr points to the next character to
11499 * be processed in the current argument. If shellparam.optnext is NULL,
11500 * then it's the first time getopts has been called.
11501 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011502static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000011503getoptscmd(int argc, char **argv)
11504{
11505 char **optbase;
11506
11507 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011508 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011509 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000011510 optbase = shellparam.p;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011511 if ((unsigned)shellparam.optind > shellparam.nparam + 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000011512 shellparam.optind = 1;
11513 shellparam.optoff = -1;
11514 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011515 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011516 optbase = &argv[3];
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011517 if ((unsigned)shellparam.optind > argc - 2) {
Eric Andersenc470f442003-07-28 09:56:35 +000011518 shellparam.optind = 1;
11519 shellparam.optoff = -1;
11520 }
11521 }
11522
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011523 return getopts(argv[1], argv[2], optbase);
Eric Andersenc470f442003-07-28 09:56:35 +000011524}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011525#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000011526
Eric Andersencb57d552001-06-28 07:25:16 +000011527
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011528/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000011529
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011530struct heredoc {
11531 struct heredoc *next; /* next here document in list */
11532 union node *here; /* redirection node */
11533 char *eofmark; /* string indicating end of input */
11534 smallint striptabs; /* if set, strip leading tabs */
11535};
11536
11537static smallint tokpushback; /* last token pushed back */
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011538static smallint quoteflag; /* set if (part of) last token was quoted */
11539static token_id_t lasttoken; /* last token read (integer id Txxx) */
11540static struct heredoc *heredoclist; /* list of here documents to read */
11541static char *wordtext; /* text of last word returned by readtoken */
11542static struct nodelist *backquotelist;
11543static union node *redirnode;
11544static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011545
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011546static const char *
11547tokname(char *buf, int tok)
11548{
11549 if (tok < TSEMI)
Denys Vlasenko888527c2016-10-02 16:54:17 +020011550 return tokname_array[tok];
11551 sprintf(buf, "\"%s\"", tokname_array[tok]);
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011552 return buf;
11553}
11554
11555/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011556 * Called when an unexpected token is read during the parse. The argument
11557 * is the token that is expected, or -1 if more than one type of token can
11558 * occur at this point.
11559 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011560static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011561static void
11562raise_error_unexpected_syntax(int token)
11563{
11564 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011565 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011566 int l;
11567
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011568 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011569 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011570 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011571 raise_error_syntax(msg);
11572 /* NOTREACHED */
11573}
Eric Andersencb57d552001-06-28 07:25:16 +000011574
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011575/* parsing is heavily cross-recursive, need these forward decls */
11576static union node *andor(void);
11577static union node *pipeline(void);
11578static union node *parse_command(void);
11579static void parseheredoc(void);
Ron Yorstonc0e00762015-10-29 11:30:55 +000011580static int peektoken(void);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011581static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000011582
Eric Andersenc470f442003-07-28 09:56:35 +000011583static union node *
11584list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000011585{
11586 union node *n1, *n2, *n3;
11587 int tok;
11588
Eric Andersencb57d552001-06-28 07:25:16 +000011589 n1 = NULL;
11590 for (;;) {
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011591 switch (readtoken()) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011592 case TNL:
11593 if (!(nlflag & 1))
11594 break;
11595 parseheredoc();
11596 return n1;
11597
11598 case TEOF:
11599 if (!n1 && (nlflag & 1))
11600 n1 = NODE_EOF;
11601 parseheredoc();
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011602 tokpushback++;
11603 lasttoken = TEOF;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011604 return n1;
11605 }
11606
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011607 tokpushback++;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011608 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko888527c2016-10-02 16:54:17 +020011609 if (nlflag == 2 && ((1 << peektoken()) & tokendlist))
Ron Yorstonc0e00762015-10-29 11:30:55 +000011610 return n1;
11611 nlflag |= 2;
11612
Eric Andersencb57d552001-06-28 07:25:16 +000011613 n2 = andor();
11614 tok = readtoken();
11615 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000011616 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011617 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011618 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011619 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011620 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000011621 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011622 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011623 n2 = n3;
11624 }
11625 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000011626 }
11627 }
11628 if (n1 == NULL) {
11629 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011630 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011631 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011632 n3->type = NSEMI;
11633 n3->nbinary.ch1 = n1;
11634 n3->nbinary.ch2 = n2;
11635 n1 = n3;
11636 }
11637 switch (tok) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011638 case TNL:
11639 case TEOF:
11640 tokpushback = 1;
11641 /* fall through */
Eric Andersencb57d552001-06-28 07:25:16 +000011642 case TBACKGND:
11643 case TSEMI:
Eric Andersencb57d552001-06-28 07:25:16 +000011644 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011645 default:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011646 if ((nlflag & 1))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011647 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011648 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011649 return n1;
11650 }
11651 }
11652}
11653
Eric Andersenc470f442003-07-28 09:56:35 +000011654static union node *
11655andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011656{
Eric Andersencb57d552001-06-28 07:25:16 +000011657 union node *n1, *n2, *n3;
11658 int t;
11659
Eric Andersencb57d552001-06-28 07:25:16 +000011660 n1 = pipeline();
11661 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011662 t = readtoken();
11663 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000011664 t = NAND;
11665 } else if (t == TOR) {
11666 t = NOR;
11667 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011668 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011669 return n1;
11670 }
Eric Andersenc470f442003-07-28 09:56:35 +000011671 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011672 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011673 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011674 n3->type = t;
11675 n3->nbinary.ch1 = n1;
11676 n3->nbinary.ch2 = n2;
11677 n1 = n3;
11678 }
11679}
11680
Eric Andersenc470f442003-07-28 09:56:35 +000011681static union node *
11682pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011683{
Eric Andersencb57d552001-06-28 07:25:16 +000011684 union node *n1, *n2, *pipenode;
11685 struct nodelist *lp, *prev;
11686 int negate;
11687
11688 negate = 0;
11689 TRACE(("pipeline: entered\n"));
11690 if (readtoken() == TNOT) {
11691 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000011692 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011693 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011694 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011695 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011696 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011697 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000011698 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011699 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011700 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000011701 pipenode->npipe.cmdlist = lp;
11702 lp->n = n1;
11703 do {
11704 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011705 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000011706 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011707 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011708 prev->next = lp;
11709 } while (readtoken() == TPIPE);
11710 lp->next = NULL;
11711 n1 = pipenode;
11712 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011713 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011714 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011715 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000011716 n2->type = NNOT;
11717 n2->nnot.com = n1;
11718 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011719 }
11720 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000011721}
11722
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011723static union node *
11724makename(void)
11725{
11726 union node *n;
11727
Denis Vlasenko597906c2008-02-20 16:38:54 +000011728 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011729 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011730 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011731 n->narg.text = wordtext;
11732 n->narg.backquote = backquotelist;
11733 return n;
11734}
11735
11736static void
11737fixredir(union node *n, const char *text, int err)
11738{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011739 int fd;
11740
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011741 TRACE(("Fix redir %s %d\n", text, err));
11742 if (!err)
11743 n->ndup.vname = NULL;
11744
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011745 fd = bb_strtou(text, NULL, 10);
11746 if (!errno && fd >= 0)
11747 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011748 else if (LONE_DASH(text))
11749 n->ndup.dupfd = -1;
11750 else {
11751 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011752 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011753 n->ndup.vname = makename();
11754 }
11755}
11756
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011757static void
11758parsefname(void)
11759{
11760 union node *n = redirnode;
11761
Denys Vlasenkoa7328982017-07-29 19:57:28 +020011762 if (n->type == NHERE)
11763 checkkwd = CHKEOFMARK;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011764 if (readtoken() != TWORD)
11765 raise_error_unexpected_syntax(-1);
11766 if (n->type == NHERE) {
11767 struct heredoc *here = heredoc;
11768 struct heredoc *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011769
11770 if (quoteflag == 0)
11771 n->type = NXHERE;
11772 TRACE(("Here document %d\n", n->type));
Denys Vlasenko740058b2018-01-09 17:01:00 +010011773 rmescapes(wordtext, 0, NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011774 here->eofmark = wordtext;
11775 here->next = NULL;
11776 if (heredoclist == NULL)
11777 heredoclist = here;
11778 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011779 for (p = heredoclist; p->next; p = p->next)
11780 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011781 p->next = here;
11782 }
11783 } else if (n->type == NTOFD || n->type == NFROMFD) {
11784 fixredir(n, wordtext, 0);
11785 } else {
11786 n->nfile.fname = makename();
11787 }
11788}
Eric Andersencb57d552001-06-28 07:25:16 +000011789
Eric Andersenc470f442003-07-28 09:56:35 +000011790static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011791simplecmd(void)
11792{
11793 union node *args, **app;
11794 union node *n = NULL;
11795 union node *vars, **vpp;
11796 union node **rpp, *redir;
11797 int savecheckkwd;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011798 int savelinno;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011799#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000011800 smallint double_brackets_flag = 0;
11801#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011802 IF_BASH_FUNCTION(smallint function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011803
11804 args = NULL;
11805 app = &args;
11806 vars = NULL;
11807 vpp = &vars;
11808 redir = NULL;
11809 rpp = &redir;
11810
11811 savecheckkwd = CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011812 savelinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011813 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000011814 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011815 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000011816 t = readtoken();
11817 switch (t) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011818#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000011819 case TFUNCTION:
11820 if (peektoken() != TWORD)
11821 raise_error_unexpected_syntax(TWORD);
11822 function_flag = 1;
11823 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011824#endif
11825#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000011826 case TAND: /* "&&" */
11827 case TOR: /* "||" */
11828 if (!double_brackets_flag) {
11829 tokpushback = 1;
11830 goto out;
11831 }
Denys Vlasenkod2241f52020-10-31 03:34:07 +010011832 /* pass "&&" or "||" to [[ ]] as literal args */
11833 wordtext = (char *) (t == TAND ? "&&" : "||");
Denis Vlasenko80591b02008-03-25 07:49:43 +000011834#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011835 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000011836 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011837 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011838 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011839 n->narg.text = wordtext;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011840#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000011841 if (strcmp("[[", wordtext) == 0)
11842 double_brackets_flag = 1;
11843 else if (strcmp("]]", wordtext) == 0)
11844 double_brackets_flag = 0;
11845#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011846 n->narg.backquote = backquotelist;
11847 if (savecheckkwd && isassignment(wordtext)) {
11848 *vpp = n;
11849 vpp = &n->narg.next;
11850 } else {
11851 *app = n;
11852 app = &n->narg.next;
11853 savecheckkwd = 0;
11854 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011855#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000011856 if (function_flag) {
11857 checkkwd = CHKNL | CHKKWD;
11858 switch (peektoken()) {
11859 case TBEGIN:
11860 case TIF:
11861 case TCASE:
11862 case TUNTIL:
11863 case TWHILE:
11864 case TFOR:
11865 goto do_func;
11866 case TLP:
11867 function_flag = 0;
11868 break;
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020011869# if BASH_TEST2
Ron Yorston95ebcf72015-11-03 09:42:23 +000011870 case TWORD:
11871 if (strcmp("[[", wordtext) == 0)
11872 goto do_func;
11873 /* fall through */
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020011874# endif
Ron Yorston95ebcf72015-11-03 09:42:23 +000011875 default:
11876 raise_error_unexpected_syntax(-1);
11877 }
11878 }
11879#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011880 break;
11881 case TREDIR:
11882 *rpp = n = redirnode;
11883 rpp = &n->nfile.next;
11884 parsefname(); /* read name of redirection file */
11885 break;
11886 case TLP:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011887 IF_BASH_FUNCTION(do_func:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011888 if (args && app == &args->narg.next
11889 && !vars && !redir
11890 ) {
11891 struct builtincmd *bcmd;
11892 const char *name;
11893
11894 /* We have a function */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011895 if (IF_BASH_FUNCTION(!function_flag &&) readtoken() != TRP)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011896 raise_error_unexpected_syntax(TRP);
11897 name = n->narg.text;
11898 if (!goodname(name)
11899 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
11900 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011901 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011902 }
11903 n->type = NDEFUN;
11904 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011905 n->ndefun.text = n->narg.text;
11906 n->ndefun.linno = g_parsefile->linno;
11907 n->ndefun.body = parse_command();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011908 return n;
11909 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011910 IF_BASH_FUNCTION(function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011911 /* fall through */
11912 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011913 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011914 goto out;
11915 }
11916 }
11917 out:
11918 *app = NULL;
11919 *vpp = NULL;
11920 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011921 n = stzalloc(sizeof(struct ncmd));
Denys Vlasenko57b7efb2018-04-10 01:20:26 +020011922 if (NCMD != 0)
11923 n->type = NCMD;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011924 n->ncmd.linno = savelinno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011925 n->ncmd.args = args;
11926 n->ncmd.assign = vars;
11927 n->ncmd.redirect = redir;
11928 return n;
11929}
11930
11931static union node *
11932parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011933{
Eric Andersencb57d552001-06-28 07:25:16 +000011934 union node *n1, *n2;
11935 union node *ap, **app;
11936 union node *cp, **cpp;
11937 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000011938 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000011939 int t;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011940 int savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000011941
11942 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000011943 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000011944
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011945 savelinno = g_parsefile->linno;
11946
Eric Andersencb57d552001-06-28 07:25:16 +000011947 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000011948 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011949 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000011950 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000011951 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011952 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000011953 n1->type = NIF;
11954 n1->nif.test = list(0);
11955 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011956 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000011957 n1->nif.ifpart = list(0);
11958 n2 = n1;
11959 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011960 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000011961 n2 = n2->nif.elsepart;
11962 n2->type = NIF;
11963 n2->nif.test = list(0);
11964 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011965 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000011966 n2->nif.ifpart = list(0);
11967 }
11968 if (lasttoken == TELSE)
11969 n2->nif.elsepart = list(0);
11970 else {
11971 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011972 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011973 }
Eric Andersenc470f442003-07-28 09:56:35 +000011974 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000011975 break;
11976 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000011977 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000011978 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011979 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011980 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000011981 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011982 got = readtoken();
11983 if (got != TDO) {
Denys Vlasenko888527c2016-10-02 16:54:17 +020011984 TRACE(("expecting DO got '%s' %s\n", tokname_array[got],
Denis Vlasenko131ae172007-02-18 13:00:19 +000011985 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011986 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000011987 }
11988 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000011989 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000011990 break;
11991 }
11992 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011993 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000011994 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011995 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000011996 n1->type = NFOR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011997 n1->nfor.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000011998 n1->nfor.var = wordtext;
Ron Yorstonab80e012015-08-03 13:46:00 +010011999 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012000 if (readtoken() == TIN) {
12001 app = &ap;
12002 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012003 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012004 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012005 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012006 n2->narg.text = wordtext;
12007 n2->narg.backquote = backquotelist;
12008 *app = n2;
12009 app = &n2->narg.next;
12010 }
12011 *app = NULL;
12012 n1->nfor.args = ap;
12013 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012014 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000012015 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012016 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012017 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012018 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012019 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012020 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000012021 n1->nfor.args = n2;
12022 /*
12023 * Newline or semicolon here is optional (but note
12024 * that the original Bourne shell only allowed NL).
12025 */
Ron Yorstonab80e012015-08-03 13:46:00 +010012026 if (lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012027 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012028 }
Eric Andersenc470f442003-07-28 09:56:35 +000012029 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012030 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012031 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012032 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012033 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012034 break;
12035 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012036 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000012037 n1->type = NCASE;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012038 n1->ncase.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012039 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012040 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012041 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012042 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012043 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012044 n2->narg.text = wordtext;
12045 n2->narg.backquote = backquotelist;
Ron Yorston383b8852015-08-03 13:46:25 +010012046 checkkwd = CHKNL | CHKKWD | CHKALIAS;
12047 if (readtoken() != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012048 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000012049 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012050 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000012051 checkkwd = CHKNL | CHKKWD;
12052 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012053 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012054 if (lasttoken == TLP)
12055 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012056 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000012057 cp->type = NCLIST;
12058 app = &cp->nclist.pattern;
12059 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012060 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012061 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012062 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012063 ap->narg.text = wordtext;
12064 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000012065 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000012066 break;
12067 app = &ap->narg.next;
12068 readtoken();
12069 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000012070 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012071 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012072 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000012073 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000012074
Eric Andersenc470f442003-07-28 09:56:35 +000012075 cpp = &cp->nclist.next;
12076
12077 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012078 t = readtoken();
12079 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012080 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012081 raise_error_unexpected_syntax(TENDCASE);
12082 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000012083 }
Eric Andersenc470f442003-07-28 09:56:35 +000012084 }
Eric Andersencb57d552001-06-28 07:25:16 +000012085 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012086 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000012087 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012088 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012089 n1->type = NSUBSHELL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012090 n1->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012091 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012092 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012093 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000012094 break;
12095 case TBEGIN:
12096 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012097 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000012098 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012099 IF_BASH_FUNCTION(case TFUNCTION:)
Eric Andersencb57d552001-06-28 07:25:16 +000012100 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000012101 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012102 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012103 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000012104 }
12105
Eric Andersenc470f442003-07-28 09:56:35 +000012106 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012107 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000012108
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012109 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000012110 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000012111 checkkwd = CHKKWD | CHKALIAS;
12112 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012113 while (readtoken() == TREDIR) {
12114 *rpp = n2 = redirnode;
12115 rpp = &n2->nfile.next;
12116 parsefname();
12117 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012118 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012119 *rpp = NULL;
12120 if (redir) {
12121 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012122 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012123 n2->type = NREDIR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012124 n2->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012125 n2->nredir.n = n1;
12126 n1 = n2;
12127 }
12128 n1->nredir.redirect = redir;
12129 }
Eric Andersencb57d552001-06-28 07:25:16 +000012130 return n1;
12131}
12132
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012133#if BASH_DOLLAR_SQUOTE
Denys Vlasenko37dc08b2016-10-02 04:38:07 +020012134static int
12135decode_dollar_squote(void)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012136{
12137 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
12138 int c, cnt;
12139 char *p;
12140 char buf[4];
12141
12142 c = pgetc();
12143 p = strchr(C_escapes, c);
12144 if (p) {
12145 buf[0] = c;
12146 p = buf;
12147 cnt = 3;
12148 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
12149 do {
12150 c = pgetc();
12151 *++p = c;
12152 } while ((unsigned char)(c - '0') <= 7 && --cnt);
12153 pungetc();
12154 } else if (c == 'x') { /* \xHH */
12155 do {
12156 c = pgetc();
12157 *++p = c;
12158 } while (isxdigit(c) && --cnt);
12159 pungetc();
12160 if (cnt == 3) { /* \x but next char is "bad" */
12161 c = 'x';
12162 goto unrecognized;
12163 }
12164 } else { /* simple seq like \\ or \t */
12165 p++;
12166 }
12167 *p = '\0';
12168 p = buf;
12169 c = bb_process_escape_sequence((void*)&p);
12170 } else { /* unrecognized "\z": print both chars unless ' or " */
12171 if (c != '\'' && c != '"') {
12172 unrecognized:
12173 c |= 0x100; /* "please encode \, then me" */
12174 }
12175 }
12176 return c;
12177}
12178#endif
12179
Denys Vlasenko46999802017-07-29 21:12:29 +020012180/* Used by expandstr to get here-doc like behaviour. */
12181#define FAKEEOFMARK ((char*)(uintptr_t)1)
12182
12183static ALWAYS_INLINE int
12184realeofmark(const char *eofmark)
12185{
12186 return eofmark && eofmark != FAKEEOFMARK;
12187}
12188
Eric Andersencb57d552001-06-28 07:25:16 +000012189/*
12190 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
12191 * is not NULL, read a here document. In the latter case, eofmark is the
12192 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010012193 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000012194 * is the first character of the input token or document.
12195 *
12196 * Because C does not have internal subroutines, I have simulated them
12197 * using goto's to implement the subroutine linkage. The following macros
12198 * will run code that appears at the end of readtoken1.
12199 */
Eric Andersen2870d962001-07-02 17:27:21 +000012200#define CHECKEND() {goto checkend; checkend_return:;}
12201#define PARSEREDIR() {goto parseredir; parseredir_return:;}
12202#define PARSESUB() {goto parsesub; parsesub_return:;}
12203#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
12204#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
12205#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000012206static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010012207readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000012208{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012209 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010012210 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000012211 char *out;
Denys Vlasenko50e6d422016-09-30 11:35:54 +020012212 size_t len;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012213 struct nodelist *bqlist;
12214 smallint quotef;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012215 smallint oldstyle;
Denis Vlasenko46a53062007-09-24 18:30:02 +000012216 smallint pssyntax; /* we are expanding a prompt string */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012217 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012218 /* syntax stack */
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012219 struct synstack synbase = { };
Denys Vlasenko216913c2018-04-02 12:35:04 +020012220 struct synstack *synstack = &synbase;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012221
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012222#if ENABLE_ASH_EXPAND_PRMT
Denis Vlasenko46a53062007-09-24 18:30:02 +000012223 pssyntax = (syntax == PSSYNTAX);
12224 if (pssyntax)
12225 syntax = DQSYNTAX;
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012226#else
12227 pssyntax = 0; /* constant */
12228#endif
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012229 synstack->syntax = syntax;
12230
Denys Vlasenko216913c2018-04-02 12:35:04 +020012231 if (syntax == DQSYNTAX)
12232 synstack->dblquote = 1;
12233 quotef = 0;
12234 bqlist = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012235
12236 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012237 loop:
12238 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012239 CHECKEND(); /* set c to PEOF if at end of here document */
12240 for (;;) { /* until end of line or end of word */
12241 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012242 switch (SIT(c, synstack->syntax)) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012243 case CNL: /* '\n' */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012244 if (synstack->syntax == BASESYNTAX
12245 && !synstack->varnest
12246 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012247 goto endword; /* exit outer loop */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012248 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012249 USTPUTC(c, out);
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012250 nlprompt();
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012251 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012252 goto loop; /* continue outer loop */
12253 case CWORD:
12254 USTPUTC(c, out);
12255 break;
12256 case CCTL:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012257#if BASH_DOLLAR_SQUOTE
Denys Vlasenko958581a2010-09-12 15:04:27 +020012258 if (c == '\\' && bash_dollar_squote) {
12259 c = decode_dollar_squote();
Denys Vlasenko13f20912016-09-25 20:54:25 +020012260 if (c == '\0') {
12261 /* skip $'\000', $'\x00' (like bash) */
12262 break;
12263 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012264 if (c & 0x100) {
Denys Vlasenko13f20912016-09-25 20:54:25 +020012265 /* Unknown escape. Encode as '\z' */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012266 c = (unsigned char)c;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012267 if (eofmark == NULL || synstack->dblquote)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012268 USTPUTC(CTLESC, out);
12269 USTPUTC('\\', out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012270 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012271 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012272#endif
Denys Vlasenkoc4c20122018-04-02 13:29:20 +020012273 if (!eofmark || synstack->dblquote || synstack->varnest)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012274 USTPUTC(CTLESC, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012275 USTPUTC(c, out);
12276 break;
12277 case CBACK: /* backslash */
12278 c = pgetc_without_PEOA();
12279 if (c == PEOF) {
12280 USTPUTC(CTLESC, out);
12281 USTPUTC('\\', out);
12282 pungetc();
Denys Vlasenko958581a2010-09-12 15:04:27 +020012283 } else {
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012284 if (pssyntax && c == '$') {
Eric Andersenc470f442003-07-28 09:56:35 +000012285 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012286 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012287 }
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012288 /* Backslash is retained if we are in "str"
12289 * and next char isn't dquote-special.
12290 */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012291 if (synstack->dblquote
Denys Vlasenko958581a2010-09-12 15:04:27 +020012292 && c != '\\'
12293 && c != '`'
12294 && c != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020012295 && (c != '"' || (eofmark != NULL && !synstack->varnest))
12296 && (c != '}' || !synstack->varnest)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012297 ) {
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012298 USTPUTC(CTLESC, out); /* protect '\' from glob */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012299 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012300 }
Ron Yorston549deab2015-05-18 09:57:51 +020012301 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020012302 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012303 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012304 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012305 break;
12306 case CSQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012307 synstack->syntax = SQSYNTAX;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012308 quotemark:
12309 if (eofmark == NULL) {
12310 USTPUTC(CTLQUOTEMARK, out);
12311 }
12312 break;
12313 case CDQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012314 synstack->syntax = DQSYNTAX;
12315 synstack->dblquote = 1;
12316 toggledq:
12317 if (synstack->varnest)
12318 synstack->innerdq ^= 1;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012319 goto quotemark;
12320 case CENDQUOTE:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012321 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012322 if (eofmark != NULL && synstack->varnest == 0) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012323 USTPUTC(c, out);
Denys Vlasenko216913c2018-04-02 12:35:04 +020012324 break;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012325 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012326
12327 if (synstack->dqvarnest == 0) {
12328 synstack->syntax = BASESYNTAX;
12329 synstack->dblquote = 0;
12330 }
12331
12332 quotef = 1;
12333
12334 if (c == '"')
12335 goto toggledq;
12336
12337 goto quotemark;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012338 case CVAR: /* '$' */
12339 PARSESUB(); /* parse substitution */
12340 break;
12341 case CENDVAR: /* '}' */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012342 if (!synstack->innerdq && synstack->varnest > 0) {
12343 if (!--synstack->varnest && synstack->varpushed)
12344 synstack_pop(&synstack);
12345 else if (synstack->dqvarnest > 0)
12346 synstack->dqvarnest--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012347 c = CTLENDVAR;
12348 }
12349 USTPUTC(c, out);
12350 break;
Denys Vlasenko0b883582016-12-23 16:49:07 +010012351#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko958581a2010-09-12 15:04:27 +020012352 case CLP: /* '(' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012353 synstack->parenlevel++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012354 USTPUTC(c, out);
12355 break;
12356 case CRP: /* ')' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012357 if (synstack->parenlevel > 0) {
12358 synstack->parenlevel--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012359 } else {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012360 if (pgetc_eatbnl() == ')') {
Ron Yorstonad88bde2015-05-18 09:56:16 +020012361 c = CTLENDARI;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012362 synstack_pop(&synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012363 } else {
12364 /*
12365 * unbalanced parens
12366 * (don't 2nd guess - no error)
12367 */
12368 pungetc();
12369 }
12370 }
12371 USTPUTC(c, out);
12372 break;
12373#endif
12374 case CBQUOTE: /* '`' */
Denys Vlasenko41fddb42018-04-01 16:38:32 +020012375 if (checkkwd & CHKEOFMARK) {
12376 quotef = 1;
12377 USTPUTC('`', out);
12378 break;
12379 }
12380
Denys Vlasenko958581a2010-09-12 15:04:27 +020012381 PARSEBACKQOLD();
12382 break;
12383 case CENDFILE:
12384 goto endword; /* exit outer loop */
12385 case CIGN:
12386 break;
12387 default:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012388 if (synstack->varnest == 0) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012389#if BASH_REDIR_OUTPUT
Denys Vlasenko958581a2010-09-12 15:04:27 +020012390 if (c == '&') {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012391//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
Denys Vlasenko958581a2010-09-12 15:04:27 +020012392 if (pgetc() == '>')
12393 c = 0x100 + '>'; /* flag &> */
12394 pungetc();
12395 }
12396#endif
12397 goto endword; /* exit outer loop */
12398 }
12399 IF_ASH_ALIAS(if (c != PEOA))
12400 USTPUTC(c, out);
12401 }
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012402 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012403 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012404 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020012405
Denys Vlasenko0b883582016-12-23 16:49:07 +010012406#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko216913c2018-04-02 12:35:04 +020012407 if (synstack->syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012408 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000012409#endif
Denys Vlasenko216913c2018-04-02 12:35:04 +020012410 if (synstack->syntax != BASESYNTAX && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012411 raise_error_syntax("unterminated quoted string");
Denys Vlasenko216913c2018-04-02 12:35:04 +020012412 if (synstack->varnest != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012413 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000012414 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000012415 }
12416 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012417 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000012418 out = stackblock();
12419 if (eofmark == NULL) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012420 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000012421 && quotef == 0
12422 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012423 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012424 PARSEREDIR(); /* passed as params: out, c */
12425 lasttoken = TREDIR;
12426 return lasttoken;
12427 }
12428 /* else: non-number X seen, interpret it
12429 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000012430 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012431 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012432 }
12433 quoteflag = quotef;
12434 backquotelist = bqlist;
12435 grabstackblock(len);
12436 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012437 lasttoken = TWORD;
12438 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000012439/* end of readtoken routine */
12440
Eric Andersencb57d552001-06-28 07:25:16 +000012441/*
12442 * Check to see whether we are at the end of the here document. When this
12443 * is called, c is set to the first character of the next input line. If
12444 * we are at the end of the here document, this routine sets the c to PEOF.
12445 */
Eric Andersenc470f442003-07-28 09:56:35 +000012446checkend: {
Denys Vlasenko46999802017-07-29 21:12:29 +020012447 if (realeofmark(eofmark)) {
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012448 int markloc;
12449 char *p;
12450
Denis Vlasenko131ae172007-02-18 13:00:19 +000012451#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012452 if (c == PEOA)
12453 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000012454#endif
12455 if (striptabs) {
12456 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012457 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000012458 }
Eric Andersenc470f442003-07-28 09:56:35 +000012459 }
Eric Andersencb57d552001-06-28 07:25:16 +000012460
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012461 markloc = out - (char *)stackblock();
12462 for (p = eofmark; STPUTC(c, out), *p; p++) {
12463 if (c != *p)
12464 goto more_heredoc;
Denys Vlasenko35e349d2019-09-05 14:31:49 +020012465 /* FIXME: fails for backslash-newlined terminator:
12466 * cat <<EOF
12467 * ...
12468 * EO\
12469 * F
12470 * (see heredoc_bkslash_newline2.tests)
12471 */
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012472 c = pgetc_without_PEOA();
12473 }
12474
12475 if (c == '\n' || c == PEOF) {
12476 c = PEOF;
12477 g_parsefile->linno++;
12478 needprompt = doprompt;
12479 } else {
12480 int len_here;
12481
12482 more_heredoc:
12483 p = (char *)stackblock() + markloc + 1;
12484 len_here = out - p;
12485
12486 if (len_here) {
12487 len_here -= (c >= PEOF);
12488 c = p[-1];
12489
12490 if (len_here) {
12491 char *str;
12492
12493 str = alloca(len_here + 1);
12494 *(char *)mempcpy(str, p, len_here) = '\0';
12495
12496 pushstring(str, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000012497 }
12498 }
12499 }
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012500
12501 STADJUST((char *)stackblock() + markloc - out, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012502 }
Eric Andersenc470f442003-07-28 09:56:35 +000012503 goto checkend_return;
12504}
Eric Andersencb57d552001-06-28 07:25:16 +000012505
Eric Andersencb57d552001-06-28 07:25:16 +000012506/*
12507 * Parse a redirection operator. The variable "out" points to a string
12508 * specifying the fd to be redirected. The variable "c" contains the
12509 * first character of the redirection operator.
12510 */
Eric Andersenc470f442003-07-28 09:56:35 +000012511parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012512 /* out is already checked to be a valid number or "" */
12513 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000012514 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000012515
Denis Vlasenko597906c2008-02-20 16:38:54 +000012516 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000012517 if (c == '>') {
12518 np->nfile.fd = 1;
Denys Vlasenko220be532018-03-31 19:21:31 +020012519 c = pgetc_eatbnl();
Eric Andersenc470f442003-07-28 09:56:35 +000012520 if (c == '>')
12521 np->type = NAPPEND;
12522 else if (c == '|')
12523 np->type = NCLOBBER;
12524 else if (c == '&')
12525 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000012526 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000012527 else {
12528 np->type = NTO;
12529 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012530 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000012531 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012532#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000012533 else if (c == 0x100 + '>') { /* this flags &> redirection */
12534 np->nfile.fd = 1;
12535 pgetc(); /* this is '>', no need to check */
12536 np->type = NTO2;
12537 }
12538#endif
12539 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000012540 /*np->nfile.fd = 0; - stzalloc did it */
Denys Vlasenko220be532018-03-31 19:21:31 +020012541 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012542 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000012543 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012544 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012545 np = stzalloc(sizeof(struct nhere));
12546 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012547 }
12548 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012549 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000012550 heredoc->here = np;
Denys Vlasenko220be532018-03-31 19:21:31 +020012551 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012552 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000012553 heredoc->striptabs = 1;
12554 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012555 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012556 pungetc();
12557 }
12558 break;
12559
12560 case '&':
12561 np->type = NFROMFD;
12562 break;
12563
12564 case '>':
12565 np->type = NFROMTO;
12566 break;
12567
12568 default:
12569 np->type = NFROM;
12570 pungetc();
12571 break;
12572 }
Eric Andersencb57d552001-06-28 07:25:16 +000012573 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012574 if (fd >= 0)
12575 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000012576 redirnode = np;
12577 goto parseredir_return;
12578}
Eric Andersencb57d552001-06-28 07:25:16 +000012579
Eric Andersencb57d552001-06-28 07:25:16 +000012580/*
12581 * Parse a substitution. At this point, we have read the dollar sign
12582 * and nothing else.
12583 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012584
12585/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
12586 * (assuming ascii char codes, as the original implementation did) */
12587#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012588 (((unsigned)(c) - 33 < 32) \
12589 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000012590parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010012591 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012592 int typeloc;
Eric Andersencb57d552001-06-28 07:25:16 +000012593
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012594 c = pgetc_eatbnl();
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012595 if ((checkkwd & CHKEOFMARK)
12596 || c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012597 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000012598 ) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012599#if BASH_DOLLAR_SQUOTE
Denys Vlasenko216913c2018-04-02 12:35:04 +020012600 if (synstack->syntax != DQSYNTAX && c == '\'')
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012601 bash_dollar_squote = 1;
12602 else
12603#endif
12604 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012605 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012606 } else if (c == '(') {
12607 /* $(command) or $((arith)) */
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012608 if (pgetc_eatbnl() == '(') {
Denys Vlasenko0b883582016-12-23 16:49:07 +010012609#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000012610 PARSEARITH();
12611#else
Denys Vlasenko4f8079d2017-07-17 17:11:48 +020012612 raise_error_syntax("support for $((arith)) is disabled");
Eric Andersenc470f442003-07-28 09:56:35 +000012613#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012614 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000012615 pungetc();
12616 PARSEBACKQNEW();
12617 }
12618 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012619 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012620 smalluint newsyn = synstack->syntax;
12621
Eric Andersenc470f442003-07-28 09:56:35 +000012622 USTPUTC(CTLVAR, out);
12623 typeloc = out - (char *)stackblock();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012624 STADJUST(1, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012625 subtype = VSNORMAL;
12626 if (c == '{') {
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012627 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012628 subtype = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000012629 }
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012630 varname:
Denys Vlasenko3df14102016-10-26 16:41:13 +020012631 if (is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012632 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012633 do {
12634 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012635 c = pgetc_eatbnl();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012636 } while (is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012637 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012638 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012639 do {
12640 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012641 c = pgetc_eatbnl();
Denys Vlasenkoc2ce8882020-02-17 10:15:35 +010012642 } while (!subtype && isdigit(c));
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012643 } else if (c != '}') {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012644 /* $[{[#]]<specialchar>[}] */
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012645 int cc = c;
12646
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012647 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012648 if (!subtype && cc == '#') {
12649 subtype = VSLENGTH;
12650 if (c == '_' || isalnum(c))
12651 goto varname;
12652 cc = c;
12653 c = pgetc_eatbnl();
12654 if (cc == '}' || c != '}') {
12655 pungetc();
12656 subtype = 0;
12657 c = cc;
12658 cc = '#';
12659 }
12660 }
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012661
12662 if (!is_special(cc)) {
12663 if (subtype == VSLENGTH)
12664 subtype = 0;
12665 goto badsub;
12666 }
12667
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012668 USTPUTC(cc, out);
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012669 } else
12670 goto badsub;
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012671
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012672 if (c != '}' && subtype == VSLENGTH) {
12673 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020012674 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012675 }
Eric Andersencb57d552001-06-28 07:25:16 +000012676
Eric Andersenc470f442003-07-28 09:56:35 +000012677 if (subtype == 0) {
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012678 static const char types[] ALIGN1 = "}-+?=";
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012679 /* ${VAR...} but not $VAR or ${#VAR} */
12680 /* c == first char after VAR */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012681 int cc = c;
12682
Eric Andersenc470f442003-07-28 09:56:35 +000012683 switch (c) {
12684 case ':':
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012685 c = pgetc_eatbnl();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012686#if BASH_SUBSTR
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012687 /* This check is only needed to not misinterpret
12688 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
12689 * constructs.
12690 */
12691 if (!strchr(types, c)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012692 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012693 pungetc();
Denys Vlasenko88e15702016-10-26 01:55:56 +020012694 break; /* "goto badsub" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012695 }
12696#endif
Denys Vlasenko3df14102016-10-26 16:41:13 +020012697 subtype = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000012698 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012699 default: {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012700 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000012701 if (p == NULL)
Denys Vlasenko88e15702016-10-26 01:55:56 +020012702 break;
Denys Vlasenko3df14102016-10-26 16:41:13 +020012703 subtype |= p - types + VSNORMAL;
Eric Andersenc470f442003-07-28 09:56:35 +000012704 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012705 }
Eric Andersenc470f442003-07-28 09:56:35 +000012706 case '%':
Denys Vlasenko216913c2018-04-02 12:35:04 +020012707 case '#':
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012708 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012709 c = pgetc_eatbnl();
Denys Vlasenko216913c2018-04-02 12:35:04 +020012710 if (c == cc)
12711 subtype++;
12712 else
12713 pungetc();
12714
12715 newsyn = BASESYNTAX;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012716 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012717#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012718 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020012719 /* ${v/[/]pattern/repl} */
12720//TODO: encode pattern and repl separately.
Denys Vlasenko216913c2018-04-02 12:35:04 +020012721// Currently cases like: v=1;echo ${v/$((1/1))/ONE}
12722// are broken (should print "ONE")
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012723 subtype = VSREPLACE;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012724 newsyn = BASESYNTAX;
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012725 c = pgetc_eatbnl();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012726 if (c != '/')
Denys Vlasenko88e15702016-10-26 01:55:56 +020012727 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012728 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012729 break;
12730#endif
Eric Andersencb57d552001-06-28 07:25:16 +000012731 }
Eric Andersenc470f442003-07-28 09:56:35 +000012732 } else {
Denys Vlasenko88e15702016-10-26 01:55:56 +020012733 badsub:
Eric Andersenc470f442003-07-28 09:56:35 +000012734 pungetc();
12735 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012736
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012737 if (newsyn == ARISYNTAX)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012738 newsyn = DQSYNTAX;
12739
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012740 if ((newsyn != synstack->syntax || synstack->innerdq)
12741 && subtype != VSNORMAL
12742 ) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012743 synstack_push(&synstack,
12744 synstack->prev ?: alloca(sizeof(*synstack)),
12745 newsyn);
12746
12747 synstack->varpushed = 1;
12748 synstack->dblquote = newsyn != BASESYNTAX;
12749 }
12750
Denys Vlasenko3df14102016-10-26 16:41:13 +020012751 ((unsigned char *)stackblock())[typeloc] = subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012752 if (subtype != VSNORMAL) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012753 synstack->varnest++;
12754 if (synstack->dblquote)
12755 synstack->dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000012756 }
Denys Vlasenko88e15702016-10-26 01:55:56 +020012757 STPUTC('=', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012758 }
Eric Andersenc470f442003-07-28 09:56:35 +000012759 goto parsesub_return;
12760}
Eric Andersencb57d552001-06-28 07:25:16 +000012761
Eric Andersencb57d552001-06-28 07:25:16 +000012762/*
12763 * Called to parse command substitutions. Newstyle is set if the command
12764 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
12765 * list of commands (passed by reference), and savelen is the number of
12766 * characters on the top of the stack which must be preserved.
12767 */
Eric Andersenc470f442003-07-28 09:56:35 +000012768parsebackq: {
12769 struct nodelist **nlpp;
Eric Andersenc470f442003-07-28 09:56:35 +000012770 union node *n;
Ron Yorston072fc602015-07-01 16:46:18 +010012771 char *str;
Eric Andersenc470f442003-07-28 09:56:35 +000012772 size_t savelen;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012773 struct heredoc *saveheredoclist;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012774 smallint saveprompt = 0;
12775
Eric Andersenc470f442003-07-28 09:56:35 +000012776 str = NULL;
12777 savelen = out - (char *)stackblock();
12778 if (savelen > 0) {
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012779 /*
12780 * FIXME: this can allocate very large block on stack and SEGV.
12781 * Example:
12782 * echo "..<100kbytes>..`true` $(true) `true` ..."
Denys Vlasenko73737592016-09-17 20:58:22 +020012783 * allocates 100kb for every command subst. With about
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012784 * a hundred command substitutions stack overflows.
12785 * With larger prepended string, SEGV happens sooner.
12786 */
Ron Yorston072fc602015-07-01 16:46:18 +010012787 str = alloca(savelen);
Eric Andersenc470f442003-07-28 09:56:35 +000012788 memcpy(str, stackblock(), savelen);
12789 }
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012790
Eric Andersenc470f442003-07-28 09:56:35 +000012791 if (oldstyle) {
12792 /* We must read until the closing backquote, giving special
Denys Vlasenko60cb48c2013-01-14 15:57:44 +010012793 * treatment to some slashes, and then push the string and
12794 * reread it as input, interpreting it normally.
12795 */
Eric Andersenc470f442003-07-28 09:56:35 +000012796 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000012797 size_t psavelen;
12798 char *pstr;
12799
Eric Andersenc470f442003-07-28 09:56:35 +000012800 STARTSTACKSTR(pout);
12801 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012802 int pc;
12803
12804 setprompt_if(needprompt, 2);
Denys Vlasenko220be532018-03-31 19:21:31 +020012805 pc = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012806 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000012807 case '`':
12808 goto done;
12809
12810 case '\\':
Denys Vlasenko777a6352020-09-29 16:25:32 +020012811 pc = pgetc(); /* not pgetc_eatbnl! */
Eric Andersenc470f442003-07-28 09:56:35 +000012812 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020012813 && (!synstack->dblquote || pc != '"')
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010012814 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000012815 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010012816 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010012817 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000012818 break;
12819 }
12820 /* fall through */
12821
12822 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012823 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012824 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000012825
12826 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012827 nlnoprompt();
Eric Andersenc470f442003-07-28 09:56:35 +000012828 break;
12829
12830 default:
12831 break;
12832 }
12833 STPUTC(pc, pout);
12834 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012835 done:
Eric Andersenc470f442003-07-28 09:56:35 +000012836 STPUTC('\0', pout);
12837 psavelen = pout - (char *)stackblock();
12838 if (psavelen > 0) {
12839 pstr = grabstackstr(pout);
12840 setinputstring(pstr);
12841 }
12842 }
12843 nlpp = &bqlist;
12844 while (*nlpp)
12845 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012846 *nlpp = stzalloc(sizeof(**nlpp));
12847 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012848
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012849 saveheredoclist = heredoclist;
12850 heredoclist = NULL;
12851
Eric Andersenc470f442003-07-28 09:56:35 +000012852 if (oldstyle) {
12853 saveprompt = doprompt;
12854 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012855 }
12856
Eric Andersenc470f442003-07-28 09:56:35 +000012857 n = list(2);
12858
12859 if (oldstyle)
12860 doprompt = saveprompt;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012861 else {
12862 if (readtoken() != TRP)
12863 raise_error_unexpected_syntax(TRP);
12864 setinputstring(nullstr);
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012865 }
12866
Denys Vlasenko9a1a6592020-02-22 16:39:27 +010012867 parseheredoc();
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012868 heredoclist = saveheredoclist;
Eric Andersenc470f442003-07-28 09:56:35 +000012869
12870 (*nlpp)->n = n;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012871 /* Start reading from old file again. */
12872 popfile();
12873 /* Ignore any pushed back tokens left from the backquote parsing. */
12874 if (oldstyle)
Eric Andersenc470f442003-07-28 09:56:35 +000012875 tokpushback = 0;
Denys Vlasenkoc55847f2020-02-17 15:59:08 +010012876 out = growstackto(savelen + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012877 if (str) {
12878 memcpy(out, str, savelen);
12879 STADJUST(savelen, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012880 }
Ron Yorston549deab2015-05-18 09:57:51 +020012881 USTPUTC(CTLBACKQ, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012882 if (oldstyle)
12883 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012884 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000012885}
12886
Denys Vlasenko0b883582016-12-23 16:49:07 +010012887#if ENABLE_FEATURE_SH_MATH
Eric Andersencb57d552001-06-28 07:25:16 +000012888/*
12889 * Parse an arithmetic expansion (indicate start of one and set state)
12890 */
Eric Andersenc470f442003-07-28 09:56:35 +000012891parsearith: {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012892
12893 synstack_push(&synstack,
12894 synstack->prev ?: alloca(sizeof(*synstack)),
12895 ARISYNTAX);
12896 synstack->dblquote = 1;
Ron Yorstonad88bde2015-05-18 09:56:16 +020012897 USTPUTC(CTLARI, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012898 goto parsearith_return;
12899}
12900#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012901} /* end of readtoken */
12902
Eric Andersencb57d552001-06-28 07:25:16 +000012903/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012904 * Read the next input token.
12905 * If the token is a word, we set backquotelist to the list of cmds in
12906 * backquotes. We set quoteflag to true if any part of the word was
12907 * quoted.
12908 * If the token is TREDIR, then we set redirnode to a structure containing
12909 * the redirection.
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012910 *
12911 * [Change comment: here documents and internal procedures]
12912 * [Readtoken shouldn't have any arguments. Perhaps we should make the
12913 * word parsing code into a separate routine. In this case, readtoken
12914 * doesn't need to have any internal procedures, but parseword does.
12915 * We could also make parseoperator in essence the main routine, and
12916 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000012917 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012918#define NEW_xxreadtoken
12919#ifdef NEW_xxreadtoken
12920/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012921static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000012922 '\n', '(', ')', /* singles */
12923 '&', '|', ';', /* doubles */
12924 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012925};
Eric Andersencb57d552001-06-28 07:25:16 +000012926
Denis Vlasenko834dee72008-10-07 09:18:30 +000012927#define xxreadtoken_singles 3
12928#define xxreadtoken_doubles 3
12929
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012930static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012931 TNL, TLP, TRP, /* only single occurrence allowed */
12932 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
12933 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012934 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012935};
12936
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012937static int
12938xxreadtoken(void)
12939{
12940 int c;
12941
12942 if (tokpushback) {
12943 tokpushback = 0;
12944 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000012945 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012946 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012947 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020012948 c = pgetc_eatbnl();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000012949 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012950 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012951
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012952 if (c == '#') {
12953 while ((c = pgetc()) != '\n' && c != PEOF)
12954 continue;
12955 pungetc();
12956 } else if (c == '\\') {
Denys Vlasenko220be532018-03-31 19:21:31 +020012957 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012958 } else {
12959 const char *p;
12960
12961 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
12962 if (c != PEOF) {
12963 if (c == '\n') {
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012964 nlnoprompt();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012965 }
12966
12967 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000012968 if (p == NULL)
12969 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012970
Denis Vlasenko834dee72008-10-07 09:18:30 +000012971 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denys Vlasenko1e5111b2018-04-01 03:04:55 +020012972 int cc = pgetc_eatbnl();
Denis Vlasenko834dee72008-10-07 09:18:30 +000012973 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012974 p += xxreadtoken_doubles + 1;
12975 } else {
12976 pungetc();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012977#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000012978 if (c == '&' && cc == '>') /* &> */
12979 break; /* return readtoken1(...) */
12980#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012981 }
12982 }
12983 }
12984 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
12985 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012986 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012987 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000012988
12989 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012990}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012991#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012992#define RETURN(token) return lasttoken = token
12993static int
12994xxreadtoken(void)
12995{
12996 int c;
12997
12998 if (tokpushback) {
12999 tokpushback = 0;
13000 return lasttoken;
13001 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013002 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013003 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013004 c = pgetc_eatbnl();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013005 switch (c) {
13006 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010013007 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013008 continue;
13009 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000013010 while ((c = pgetc()) != '\n' && c != PEOF)
13011 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013012 pungetc();
13013 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013014 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013015 nlnoprompt();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013016 RETURN(TNL);
13017 case PEOF:
13018 RETURN(TEOF);
13019 case '&':
Denys Vlasenko220be532018-03-31 19:21:31 +020013020 if (pgetc_eatbnl() == '&')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013021 RETURN(TAND);
13022 pungetc();
13023 RETURN(TBACKGND);
13024 case '|':
Denys Vlasenko220be532018-03-31 19:21:31 +020013025 if (pgetc_eatbnl() == '|')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013026 RETURN(TOR);
13027 pungetc();
13028 RETURN(TPIPE);
13029 case ';':
Denys Vlasenko220be532018-03-31 19:21:31 +020013030 if (pgetc_eatbnl() == ';')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013031 RETURN(TENDCASE);
13032 pungetc();
13033 RETURN(TSEMI);
13034 case '(':
13035 RETURN(TLP);
13036 case ')':
13037 RETURN(TRP);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013038 }
Denys Vlasenko220be532018-03-31 19:21:31 +020013039 break;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013040 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013041 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
13042#undef RETURN
13043}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013044#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013045
13046static int
13047readtoken(void)
13048{
13049 int t;
Ron Yorston713f07d2015-10-29 16:44:56 +000013050 int kwd = checkkwd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013051#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013052 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013053#endif
13054
13055#if ENABLE_ASH_ALIAS
13056 top:
13057#endif
13058
13059 t = xxreadtoken();
13060
13061 /*
13062 * eat newlines
13063 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013064 if (kwd & CHKNL) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013065 while (t == TNL) {
13066 parseheredoc();
13067 t = xxreadtoken();
13068 }
13069 }
13070
13071 if (t != TWORD || quoteflag) {
13072 goto out;
13073 }
13074
13075 /*
13076 * check for keywords
13077 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013078 if (kwd & CHKKWD) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013079 const char *const *pp;
13080
13081 pp = findkwd(wordtext);
13082 if (pp) {
13083 lasttoken = t = pp - tokname_array;
Denys Vlasenko888527c2016-10-02 16:54:17 +020013084 TRACE(("keyword '%s' recognized\n", tokname_array[t]));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013085 goto out;
13086 }
13087 }
13088
13089 if (checkkwd & CHKALIAS) {
13090#if ENABLE_ASH_ALIAS
13091 struct alias *ap;
13092 ap = lookupalias(wordtext, 1);
13093 if (ap != NULL) {
13094 if (*ap->val) {
13095 pushstring(ap->val, ap);
13096 }
13097 goto top;
13098 }
13099#endif
13100 }
13101 out:
13102 checkkwd = 0;
13103#if DEBUG
13104 if (!alreadyseen)
Denys Vlasenko888527c2016-10-02 16:54:17 +020013105 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013106 else
Denys Vlasenko888527c2016-10-02 16:54:17 +020013107 TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013108#endif
13109 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000013110}
13111
Ron Yorstonc0e00762015-10-29 11:30:55 +000013112static int
Ron Yorston6bd2fab2015-10-29 11:30:22 +000013113peektoken(void)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013114{
13115 int t;
13116
13117 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013118 tokpushback = 1;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013119 return t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013120}
Eric Andersencb57d552001-06-28 07:25:16 +000013121
13122/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013123 * Read and parse a command. Returns NODE_EOF on end of file.
13124 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000013125 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013126static union node *
13127parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000013128{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013129 tokpushback = 0;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013130 checkkwd = 0;
13131 heredoclist = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013132 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013133 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013134 needprompt = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013135 return list(1);
13136}
13137
13138/*
13139 * Input any here documents.
13140 */
13141static void
13142parseheredoc(void)
13143{
13144 struct heredoc *here;
13145 union node *n;
13146
13147 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000013148 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013149
13150 while (here) {
Christoph Schulz03ad7ae2018-11-20 17:45:52 +010013151 tokpushback = 0;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013152 setprompt_if(needprompt, 2);
Denys Vlasenkoacf79f92020-02-14 16:12:06 +010013153 if (here->here->type == NHERE)
13154 readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs);
13155 else
13156 readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000013157 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013158 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013159 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013160 n->narg.text = wordtext;
13161 n->narg.backquote = backquotelist;
13162 here->here->nhere.doc = n;
13163 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000013164 }
Eric Andersencb57d552001-06-28 07:25:16 +000013165}
13166
13167
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013168static const char *
Denys Vlasenko46999802017-07-29 21:12:29 +020013169expandstr(const char *ps, int syntax_type)
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013170{
13171 union node n;
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013172 int saveprompt;
Ron Yorston48645b82019-04-18 09:48:13 +010013173 struct parsefile *file_stop = g_parsefile;
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013174 volatile int saveint;
13175 struct jmploc *volatile savehandler = exception_handler;
13176 struct jmploc jmploc;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013177 const char *volatile result;
13178 int err;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013179
Denys Vlasenko46999802017-07-29 21:12:29 +020013180 /* XXX Fix (char *) cast. */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013181 setinputstring((char *)ps);
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013182
13183 saveprompt = doprompt;
13184 doprompt = 0;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013185 result = ps;
13186
13187 SAVE_INT(saveint);
13188 err = setjmp(jmploc.loc);
13189 if (err)
13190 goto out;
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013191
13192 /* readtoken1() might die horribly.
Denys Vlasenko3c183a82017-10-12 19:35:42 +020013193 * Try a prompt with syntactically wrong command:
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013194 * PS1='$(date "+%H:%M:%S) > '
13195 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013196 exception_handler = &jmploc;
13197 readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013198
13199 n.narg.type = NARG;
13200 n.narg.next = NULL;
13201 n.narg.text = wordtext;
13202 n.narg.backquote = backquotelist;
13203
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013204 /* expandarg() might fail too:
13205 * PS1='$((123+))'
13206 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013207 expandarg(&n, NULL, EXP_QUOTED);
13208 result = stackblock();
13209
13210out:
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013211 exception_handler = savehandler;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013212 if (err && exception_type != EXERROR)
13213 longjmp(exception_handler->loc, 1);
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013214 RESTORE_INT(saveint);
13215
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013216 doprompt = saveprompt;
13217 /* Try: PS1='`xxx(`' */
13218 unwindfiles(file_stop);
13219
13220 return result;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013221}
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013222
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013223static inline int
13224parser_eof(void)
13225{
13226 return tokpushback && lasttoken == TEOF;
13227}
13228
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013229/*
13230 * Execute a command or commands contained in a string.
13231 */
13232static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013233evalstring(char *s, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000013234{
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013235 struct jmploc *volatile savehandler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013236 struct jmploc jmploc;
13237 int ex;
13238
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013239 union node *n;
13240 struct stackmark smark;
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013241 int status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013242
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013243 s = sstrdup(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013244 setinputstring(s);
13245 setstackmark(&smark);
13246
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013247 status = 0;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013248 /* On exception inside execution loop, we must popfile().
13249 * Try interactively:
13250 * readonly a=a
13251 * command eval "a=b" # throws "is read only" error
13252 * "command BLTIN" is not supposed to abort (even in non-interactive use).
13253 * But if we skip popfile(), we hit EOF in eval's string, and exit.
13254 */
13255 savehandler = exception_handler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013256 ex = setjmp(jmploc.loc);
13257 if (ex)
13258 goto out;
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013259 exception_handler = &jmploc;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013260
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013261 while ((n = parsecmd(0)) != NODE_EOF) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013262 int i;
13263
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013264 i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013265 if (n)
13266 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013267 popstackmark(&smark);
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013268 if (evalskip)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013269 break;
13270 }
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013271 out:
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013272 popstackmark(&smark);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013273 popfile();
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013274 stunalloc(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013275
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013276 exception_handler = savehandler;
13277 if (ex)
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020013278 longjmp(exception_handler->loc, ex);
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013279
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013280 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000013281}
13282
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013283/*
13284 * The eval command.
13285 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013286static int FAST_FUNC
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013287evalcmd(int argc UNUSED_PARAM, char **argv, int flags)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013288{
13289 char *p;
13290 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013291
Denis Vlasenko68404f12008-03-17 09:00:54 +000013292 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013293 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013294 argv += 2;
13295 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013296 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013297 for (;;) {
13298 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013299 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013300 if (p == NULL)
13301 break;
13302 STPUTC(' ', concat);
13303 }
13304 STPUTC('\0', concat);
13305 p = grabstackstr(concat);
13306 }
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013307 return evalstring(p, flags & EV_TESTED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013308 }
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013309 return 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013310}
13311
13312/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010013313 * Read and execute commands.
13314 * "Top" is nonzero for the top level command loop;
13315 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013316 */
13317static int
13318cmdloop(int top)
13319{
13320 union node *n;
13321 struct stackmark smark;
13322 int inter;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013323 int status = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013324 int numeof = 0;
13325
13326 TRACE(("cmdloop(%d) called\n", top));
13327 for (;;) {
13328 int skip;
13329
13330 setstackmark(&smark);
13331#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000013332 if (doing_jobctl)
Denys Vlasenko9c541002015-10-07 15:44:36 +020013333 showjobs(SHOW_CHANGED|SHOW_STDERR);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013334#endif
13335 inter = 0;
13336 if (iflag && top) {
13337 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013338 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013339 }
13340 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020013341#if DEBUG
13342 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020013343 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000013344#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013345 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013346 if (!top || numeof >= 50)
13347 break;
13348 if (!stoppedjobs()) {
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013349 if (!Iflag) {
13350 if (iflag) {
13351 newline_and_flush(stderr);
13352 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013353 break;
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013354 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013355 out2str("\nUse \"exit\" to leave shell.\n");
13356 }
13357 numeof++;
13358 } else if (nflag == 0) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013359 int i;
13360
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000013361 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13362 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013363 numeof = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013364 i = evaltree(n, 0);
13365 if (n)
13366 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013367 }
13368 popstackmark(&smark);
13369 skip = evalskip;
13370
13371 if (skip) {
Denys Vlasenkocd24a502020-02-20 16:47:01 +010013372 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denys Vlasenko0840c912016-10-01 15:27:44 +020013373 break;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013374 }
13375 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013376 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013377}
13378
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013379/*
13380 * Take commands from a file. To be compatible we should do a path
13381 * search for the file, which is necessary to find sub-commands.
13382 */
13383static char *
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013384find_dot_file(char *basename)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013385{
13386 char *fullname;
13387 const char *path = pathval();
13388 struct stat statb;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013389 int len;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013390
13391 /* don't try this for absolute or relative paths */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013392 if (strchr(basename, '/'))
13393 return basename;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013394
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013395 while ((len = padvance(&path, basename)) >= 0) {
13396 fullname = stackblock();
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013397 if ((!pathopt || *pathopt == 'f')
13398 && !stat(fullname, &statb) && S_ISREG(statb.st_mode)
13399 ) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013400 /* This will be freed by the caller. */
13401 return stalloc(len);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013402 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013403 }
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013404 /* not found in PATH */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013405
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013406#if ENABLE_ASH_BASH_SOURCE_CURDIR
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013407 return basename;
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013408#else
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013409 ash_msg_and_raise_error("%s: not found", basename);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013410 /* NOTREACHED */
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013411#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013412}
13413
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013414static int FAST_FUNC
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013415dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013416{
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013417 /* "false; . empty_file; echo $?" should print 0, not 1: */
13418 int status = 0;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013419 char *fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013420 char **argv;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013421 char *args_need_save;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013422 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013423
Denys Vlasenko981a0562017-07-26 19:53:11 +020013424//???
13425// struct strlist *sp;
13426// for (sp = cmdenviron; sp; sp = sp->next)
13427// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013428
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013429 nextopt(nullstr); /* handle possible "--" */
13430 argv = argptr;
13431
13432 if (!argv[0]) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013433 /* bash says: "bash: .: filename argument required" */
13434 return 2; /* bash compat */
13435 }
13436
Denys Vlasenko091f8312013-03-17 14:25:22 +010013437 /* This aborts if file isn't found, which is POSIXly correct.
13438 * bash returns exitcode 1 instead.
13439 */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013440 fullname = find_dot_file(argv[0]);
13441 argv++;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013442 args_need_save = argv[0];
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010013443 if (args_need_save) { /* ". FILE ARGS", and ARGS are not empty */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013444 int argc;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013445 saveparam = shellparam;
13446 shellparam.malloced = 0;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013447 argc = 1;
13448 while (argv[argc])
13449 argc++;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013450 shellparam.nparam = argc;
13451 shellparam.p = argv;
13452 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013453
Denys Vlasenko091f8312013-03-17 14:25:22 +010013454 /* This aborts if file can't be opened, which is POSIXly correct.
13455 * bash returns exitcode 1 instead.
13456 */
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013457 setinputfile(fullname, INPUT_PUSH_FILE);
13458 commandname = fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013459 status = cmdloop(0);
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013460 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013461
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013462 if (args_need_save) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013463 freeparam(&shellparam);
13464 shellparam = saveparam;
13465 };
13466
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013467 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013468}
13469
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013470static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013471exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013472{
13473 if (stoppedjobs())
13474 return 0;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013475
Denys Vlasenko970470e2020-02-14 17:32:22 +010013476 if (argv[1])
13477 savestatus = number(argv[1]);
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013478
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013479 raise_exception(EXEXIT);
13480 /* NOTREACHED */
13481}
13482
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013483/*
13484 * Read a file containing shell functions.
13485 */
13486static void
13487readcmdfile(char *name)
13488{
13489 setinputfile(name, INPUT_PUSH_FILE);
13490 cmdloop(0);
13491 popfile();
13492}
13493
13494
Denis Vlasenkocc571512007-02-23 21:10:35 +000013495/* ============ find_command inplementation */
13496
13497/*
13498 * Resolve a command name. If you change this routine, you may have to
13499 * change the shellexec routine as well.
13500 */
13501static void
13502find_command(char *name, struct cmdentry *entry, int act, const char *path)
13503{
13504 struct tblentry *cmdp;
13505 int idx;
13506 int prev;
13507 char *fullname;
13508 struct stat statb;
13509 int e;
13510 int updatetbl;
13511 struct builtincmd *bcmd;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013512 int len;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013513
13514 /* If name contains a slash, don't use PATH or hash table */
13515 if (strchr(name, '/') != NULL) {
13516 entry->u.index = -1;
13517 if (act & DO_ABS) {
13518 while (stat(name, &statb) < 0) {
13519#ifdef SYSV
13520 if (errno == EINTR)
13521 continue;
13522#endif
13523 entry->cmdtype = CMDUNKNOWN;
13524 return;
13525 }
13526 }
13527 entry->cmdtype = CMDNORMAL;
13528 return;
13529 }
13530
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013531/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013532
13533 updatetbl = (path == pathval());
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013534 if (!updatetbl)
Denis Vlasenkocc571512007-02-23 21:10:35 +000013535 act |= DO_ALTPATH;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013536
13537 /* If name is in the table, check answer will be ok */
13538 cmdp = cmdlookup(name, 0);
13539 if (cmdp != NULL) {
13540 int bit;
13541
13542 switch (cmdp->cmdtype) {
13543 default:
13544#if DEBUG
13545 abort();
13546#endif
13547 case CMDNORMAL:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013548 bit = DO_ALTPATH | DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013549 break;
13550 case CMDFUNCTION:
13551 bit = DO_NOFUNC;
13552 break;
13553 case CMDBUILTIN:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013554 bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013555 break;
13556 }
13557 if (act & bit) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013558 if (act & bit & DO_REGBLTIN)
13559 goto fail;
13560
Denis Vlasenkocc571512007-02-23 21:10:35 +000013561 updatetbl = 0;
13562 cmdp = NULL;
13563 } else if (cmdp->rehash == 0)
13564 /* if not invalidated by cd, we're done */
13565 goto success;
13566 }
13567
13568 /* If %builtin not in path, check for builtin next */
13569 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013570 if (bcmd) {
13571 if (IS_BUILTIN_REGULAR(bcmd))
13572 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013573 if (act & DO_ALTPATH)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013574 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013575 if (builtinloc <= 0)
13576 goto builtin_success;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013577 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000013578
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013579 if (act & DO_REGBLTIN)
13580 goto fail;
13581
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013582#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013583 {
13584 int applet_no = find_applet_by_name(name);
13585 if (applet_no >= 0) {
13586 entry->cmdtype = CMDNORMAL;
13587 entry->u.index = -2 - applet_no;
13588 return;
13589 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013590 }
13591#endif
13592
Denis Vlasenkocc571512007-02-23 21:10:35 +000013593 /* We have to search path. */
13594 prev = -1; /* where to start */
13595 if (cmdp && cmdp->rehash) { /* doing a rehash */
13596 if (cmdp->cmdtype == CMDBUILTIN)
13597 prev = builtinloc;
13598 else
13599 prev = cmdp->param.index;
13600 }
13601
13602 e = ENOENT;
13603 idx = -1;
13604 loop:
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013605 while ((len = padvance(&path, name)) >= 0) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013606 const char *lpathopt = pathopt;
13607
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013608 fullname = stackblock();
Denis Vlasenkocc571512007-02-23 21:10:35 +000013609 idx++;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013610 if (lpathopt) {
13611 if (*lpathopt == 'b') {
Denis Vlasenkocc571512007-02-23 21:10:35 +000013612 if (bcmd)
13613 goto builtin_success;
13614 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013615 } else if (!(act & DO_NOFUNC)) {
13616 /* handled below */
13617 } else {
13618 /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013619 continue;
13620 }
13621 }
13622 /* if rehash, don't redo absolute path names */
13623 if (fullname[0] == '/' && idx <= prev) {
13624 if (idx < prev)
13625 continue;
13626 TRACE(("searchexec \"%s\": no change\n", name));
13627 goto success;
13628 }
13629 while (stat(fullname, &statb) < 0) {
13630#ifdef SYSV
13631 if (errno == EINTR)
13632 continue;
13633#endif
13634 if (errno != ENOENT && errno != ENOTDIR)
13635 e = errno;
13636 goto loop;
13637 }
13638 e = EACCES; /* if we fail, this will be the error */
13639 if (!S_ISREG(statb.st_mode))
13640 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013641 if (lpathopt) { /* this is a %func directory */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013642 stalloc(len);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000013643 /* NB: stalloc will return space pointed by fullname
13644 * (because we don't have any intervening allocations
13645 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013646 readcmdfile(fullname);
13647 cmdp = cmdlookup(name, 0);
13648 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
13649 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
13650 stunalloc(fullname);
13651 goto success;
13652 }
13653 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
13654 if (!updatetbl) {
13655 entry->cmdtype = CMDNORMAL;
13656 entry->u.index = idx;
13657 return;
13658 }
13659 INT_OFF;
13660 cmdp = cmdlookup(name, 1);
13661 cmdp->cmdtype = CMDNORMAL;
13662 cmdp->param.index = idx;
13663 INT_ON;
13664 goto success;
13665 }
13666
13667 /* We failed. If there was an entry for this command, delete it */
13668 if (cmdp && updatetbl)
13669 delete_cmd_entry();
William Pitcockd8fd88a2018-01-24 18:33:18 +010013670 if (act & DO_ERR) {
13671#if ENABLE_ASH_BASH_NOT_FOUND_HOOK
13672 struct tblentry *hookp = cmdlookup("command_not_found_handle", 0);
13673 if (hookp && hookp->cmdtype == CMDFUNCTION) {
13674 char *argv[3];
13675 argv[0] = (char*) "command_not_found_handle";
13676 argv[1] = name;
13677 argv[2] = NULL;
13678 evalfun(hookp->param.func, 2, argv, 0);
13679 entry->cmdtype = CMDUNKNOWN;
13680 return;
13681 }
13682#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000013683 ash_msg("%s: %s", name, errmsg(e, "not found"));
William Pitcockd8fd88a2018-01-24 18:33:18 +010013684 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013685 fail:
Denis Vlasenkocc571512007-02-23 21:10:35 +000013686 entry->cmdtype = CMDUNKNOWN;
13687 return;
13688
13689 builtin_success:
13690 if (!updatetbl) {
13691 entry->cmdtype = CMDBUILTIN;
13692 entry->u.cmd = bcmd;
13693 return;
13694 }
13695 INT_OFF;
13696 cmdp = cmdlookup(name, 1);
13697 cmdp->cmdtype = CMDBUILTIN;
13698 cmdp->param.cmd = bcmd;
13699 INT_ON;
13700 success:
13701 cmdp->rehash = 0;
13702 entry->cmdtype = cmdp->cmdtype;
13703 entry->u = cmdp->param;
13704}
13705
13706
Eric Andersencb57d552001-06-28 07:25:16 +000013707/*
Eric Andersencb57d552001-06-28 07:25:16 +000013708 * The trap builtin.
13709 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013710static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013711trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000013712{
13713 char *action;
13714 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013715 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013716
Eric Andersenc470f442003-07-28 09:56:35 +000013717 nextopt(nullstr);
13718 ap = argptr;
13719 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013720 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013721 char *tr = trap_ptr[signo];
13722 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013723 /* note: bash adds "SIG", but only if invoked
13724 * as "bash". If called as "sh", or if set -o posix,
13725 * then it prints short signal names.
13726 * We are printing short names: */
13727 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013728 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000013729 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013730 /* trap_ptr != trap only if we are in special-cased `trap` code.
13731 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013732 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013733 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000013734 }
13735 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013736 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013737 if (trap_ptr != trap) {
13738 free(trap_ptr);
13739 trap_ptr = trap;
13740 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013741 */
Eric Andersencb57d552001-06-28 07:25:16 +000013742 return 0;
13743 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013744
Denys Vlasenko86981e32017-07-25 20:06:17 +020013745 /* Why the second check?
13746 * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..."
13747 * In this case, NUM is signal no, not an action.
13748 */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000013749 action = NULL;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013750 if (ap[1] && !is_number(ap[0]))
Eric Andersencb57d552001-06-28 07:25:16 +000013751 action = *ap++;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013752
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013753 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013754 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013755 signo = get_signum(*ap);
Denys Vlasenko86981e32017-07-25 20:06:17 +020013756 if (signo < 0) {
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013757 /* Mimic bash message exactly */
13758 ash_msg("%s: invalid signal specification", *ap);
13759 exitcode = 1;
13760 goto next;
13761 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000013762 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000013763 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000013764 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000013765 action = NULL;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013766 else {
13767 if (action[0]) /* not NULL and not "" and not "-" */
13768 may_have_traps = 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013769 action = ckstrdup(action);
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013770 }
Eric Andersencb57d552001-06-28 07:25:16 +000013771 }
Denis Vlasenko60818682007-09-28 22:07:23 +000013772 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000013773 trap[signo] = action;
13774 if (signo != 0)
13775 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000013776 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013777 next:
Eric Andersencb57d552001-06-28 07:25:16 +000013778 ap++;
13779 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013780 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013781}
13782
Eric Andersenc470f442003-07-28 09:56:35 +000013783
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013784/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000013785
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013786#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013787static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013788helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000013789{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000013790 unsigned col;
13791 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000013792
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020013793 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000013794 "Built-in commands:\n"
13795 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000013796 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000013797 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000013798 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000013799 if (col > 60) {
13800 out1fmt("\n");
13801 col = 0;
13802 }
13803 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013804# if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000013805 {
13806 const char *a = applet_names;
13807 while (*a) {
13808 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
13809 if (col > 60) {
13810 out1fmt("\n");
13811 col = 0;
13812 }
Ron Yorston2b919582016-04-08 11:57:20 +010013813 while (*a++ != '\0')
13814 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000013815 }
13816 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013817# endif
Denys Vlasenkoebedb942016-10-02 18:45:09 +020013818 newline_and_flush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000013819 return EXIT_SUCCESS;
13820}
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013821#endif
Eric Andersenc470f442003-07-28 09:56:35 +000013822
Flemming Madsend96ffda2013-04-07 18:47:24 +020013823#if MAX_HISTORY
13824static int FAST_FUNC
13825historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13826{
Ron Yorston9f3b4102019-12-16 09:31:10 +000013827 show_history(line_input_state);
Flemming Madsend96ffda2013-04-07 18:47:24 +020013828 return EXIT_SUCCESS;
13829}
13830#endif
13831
Eric Andersencb57d552001-06-28 07:25:16 +000013832/*
Eric Andersencb57d552001-06-28 07:25:16 +000013833 * The export and readonly commands.
13834 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013835static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013836exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000013837{
13838 struct var *vp;
13839 char *name;
13840 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000013841 char **aptr;
Denys Vlasenkod5275882012-10-01 13:41:17 +020013842 char opt;
13843 int flag;
13844 int flag_off;
Eric Andersencb57d552001-06-28 07:25:16 +000013845
Denys Vlasenkod5275882012-10-01 13:41:17 +020013846 /* "readonly" in bash accepts, but ignores -n.
13847 * We do the same: it saves a conditional in nextopt's param.
13848 */
13849 flag_off = 0;
13850 while ((opt = nextopt("np")) != '\0') {
13851 if (opt == 'n')
13852 flag_off = VEXPORT;
13853 }
13854 flag = VEXPORT;
13855 if (argv[0][0] == 'r') {
13856 flag = VREADONLY;
13857 flag_off = 0; /* readonly ignores -n */
13858 }
13859 flag_off = ~flag_off;
13860
Denys Vlasenko10ad6222017-04-17 16:13:32 +020013861 /*if (opt_p_not_specified) - bash doesn't check this. Try "export -p NAME" */
Denys Vlasenkod5275882012-10-01 13:41:17 +020013862 {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013863 aptr = argptr;
13864 name = *aptr;
13865 if (name) {
13866 do {
13867 p = strchr(name, '=');
13868 if (p != NULL) {
13869 p++;
13870 } else {
13871 vp = *findvar(hashvar(name), name);
13872 if (vp) {
Denys Vlasenkod5275882012-10-01 13:41:17 +020013873 vp->flags = ((vp->flags | flag) & flag_off);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013874 continue;
13875 }
Eric Andersencb57d552001-06-28 07:25:16 +000013876 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020013877 setvar(name, p, (flag & flag_off));
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013878 } while ((name = *++aptr) != NULL);
13879 return 0;
13880 }
Eric Andersencb57d552001-06-28 07:25:16 +000013881 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020013882
13883 /* No arguments. Show the list of exported or readonly vars.
13884 * -n is ignored.
13885 */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013886 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000013887 return 0;
13888}
13889
Eric Andersencb57d552001-06-28 07:25:16 +000013890/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013891 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000013892 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013893static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013894unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000013895{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013896 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000013897
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013898 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013899 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013900 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000013901}
13902
Eric Andersencb57d552001-06-28 07:25:16 +000013903/*
Eric Andersencb57d552001-06-28 07:25:16 +000013904 * The unset builtin command. We unset the function before we unset the
13905 * variable to allow a function to be unset when there is a readonly variable
13906 * with the same name.
13907 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013908static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013909unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000013910{
13911 char **ap;
13912 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000013913 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013914
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013915 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000013916 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000013917 }
Eric Andersencb57d552001-06-28 07:25:16 +000013918
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013919 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000013920 if (flag != 'f') {
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020013921 unsetvar(*ap);
13922 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000013923 }
13924 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000013925 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000013926 }
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020013927 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013928}
13929
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013930static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013931 ' ', offsetof(struct tms, tms_utime),
13932 '\n', offsetof(struct tms, tms_stime),
13933 ' ', offsetof(struct tms, tms_cutime),
13934 '\n', offsetof(struct tms, tms_cstime),
13935 0
13936};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013937static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013938timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013939{
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013940 unsigned clk_tck;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013941 const unsigned char *p;
13942 struct tms buf;
13943
Bartosz Golaszewski5d2e4092014-06-22 14:01:13 +020013944 clk_tck = bb_clk_tck();
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013945
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013946 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013947 p = timescmd_str;
13948 do {
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013949 unsigned sec, frac;
13950 unsigned long t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013951 t = *(clock_t *)(((char *) &buf) + p[1]);
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013952 sec = t / clk_tck;
13953 frac = t % clk_tck;
13954 out1fmt("%um%u.%03us%c",
13955 sec / 60, sec % 60,
13956 (frac * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013957 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013958 p += 2;
13959 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013960
Eric Andersencb57d552001-06-28 07:25:16 +000013961 return 0;
13962}
13963
Denys Vlasenko0b883582016-12-23 16:49:07 +010013964#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000013965/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013966 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000013967 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000013968 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000013969 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000013970 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013971static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013972letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000013973{
Denis Vlasenko68404f12008-03-17 09:00:54 +000013974 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000013975
Denis Vlasenko68404f12008-03-17 09:00:54 +000013976 argv++;
13977 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000013978 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000013979 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000013980 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013981 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000013982
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000013983 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000013984}
Eric Andersenc470f442003-07-28 09:56:35 +000013985#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013986
Eric Andersenc470f442003-07-28 09:56:35 +000013987/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000013988 * The read builtin. Options:
13989 * -r Do not interpret '\' specially
13990 * -s Turn off echo (tty only)
13991 * -n NCHARS Read NCHARS max
13992 * -p PROMPT Display PROMPT on stderr (if input is from tty)
13993 * -t SECONDS Timeout after SECONDS (tty or pipe only)
13994 * -u FD Read from given FD instead of fd 0
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020013995 * -d DELIM End on DELIM char, not newline
Eric Andersenc470f442003-07-28 09:56:35 +000013996 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000013997 * TODO: bash also has:
13998 * -a ARRAY Read into array[0],[1],etc
Denis Vlasenko59f351c2008-03-25 00:07:12 +000013999 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000014000 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014001static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014002readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014003{
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014004 struct builtin_read_params params;
Denys Vlasenko73067272010-01-12 22:11:24 +010014005 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000014006 int i;
14007
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014008 memset(&params, 0, sizeof(params));
14009
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014010 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000014011 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000014012 case 'p':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014013 params.opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014014 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014015 case 'n':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014016 params.opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014017 break;
14018 case 's':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014019 params.read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000014020 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014021 case 't':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014022 params.opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014023 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014024 case 'r':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014025 params.read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000014026 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014027 case 'u':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014028 params.opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014029 break;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014030#if BASH_READ_D
14031 case 'd':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014032 params.opt_d = optionarg;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014033 break;
14034#endif
Paul Fox02eb9342005-09-07 16:56:02 +000014035 default:
14036 break;
14037 }
Eric Andersenc470f442003-07-28 09:56:35 +000014038 }
Paul Fox02eb9342005-09-07 16:56:02 +000014039
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014040 params.argv = argptr;
14041 params.setvar = setvar0;
14042 params.ifs = bltinlookup("IFS"); /* can be NULL */
14043
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014044 /* "read -s" needs to save/restore termios, can't allow ^C
14045 * to jump out of it.
14046 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014047 again:
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014048 INT_OFF;
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014049 r = shell_builtin_read(&params);
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014050 INT_ON;
Denis Vlasenko46aeab92009-03-31 19:18:17 +000014051
Denys Vlasenkof5470412017-05-22 19:34:45 +020014052 if ((uintptr_t)r == 1 && errno == EINTR) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014053 /* To get SIGCHLD: sleep 1 & read x; echo $x
14054 * Correct behavior is to not exit "read"
14055 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014056 if (pending_sig == 0)
14057 goto again;
14058 }
14059
Denys Vlasenko73067272010-01-12 22:11:24 +010014060 if ((uintptr_t)r > 1)
14061 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000014062
Denys Vlasenko73067272010-01-12 22:11:24 +010014063 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000014064}
14065
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014066static int FAST_FUNC
Denys Vlasenko6283f982015-10-07 16:56:20 +020014067umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014068{
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014069 static const char permuser[3] ALIGN1 = "ogu";
Eric Andersenc470f442003-07-28 09:56:35 +000014070
Eric Andersenc470f442003-07-28 09:56:35 +000014071 mode_t mask;
Eric Andersenc470f442003-07-28 09:56:35 +000014072 int symbolic_mode = 0;
14073
14074 while (nextopt("S") != '\0') {
14075 symbolic_mode = 1;
14076 }
14077
Denis Vlasenkob012b102007-02-19 22:43:01 +000014078 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000014079 mask = umask(0);
14080 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000014081 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000014082
Denys Vlasenko6283f982015-10-07 16:56:20 +020014083 if (*argptr == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000014084 if (symbolic_mode) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014085 char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
Eric Andersenc470f442003-07-28 09:56:35 +000014086 char *p = buf;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014087 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014088
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014089 i = 2;
14090 for (;;) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014091 *p++ = ',';
Eric Andersenc470f442003-07-28 09:56:35 +000014092 *p++ = permuser[i];
14093 *p++ = '=';
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014094 /* mask is 0..0uuugggooo. i=2 selects uuu bits */
Denys Vlasenko005c4922015-10-10 20:17:12 +020014095 if (!(mask & 0400)) *p++ = 'r';
14096 if (!(mask & 0200)) *p++ = 'w';
14097 if (!(mask & 0100)) *p++ = 'x';
14098 mask <<= 3;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014099 if (--i < 0)
14100 break;
Eric Andersenc470f442003-07-28 09:56:35 +000014101 }
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014102 *p = '\0';
Denys Vlasenko005c4922015-10-10 20:17:12 +020014103 puts(buf + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014104 } else {
Denys Vlasenkoec046f72015-10-07 17:57:53 +020014105 out1fmt("%04o\n", mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014106 }
14107 } else {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014108 char *modestr = *argptr;
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020014109 /* numeric umasks are taken as-is */
14110 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
Denys Vlasenko6283f982015-10-07 16:56:20 +020014111 if (!isdigit(modestr[0]))
14112 mask ^= 0777;
Denys Vlasenko5711a2a2015-10-07 17:55:33 +020014113 mask = bb_parse_mode(modestr, mask);
14114 if ((unsigned)mask > 0777) {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014115 ash_msg_and_raise_error("illegal mode: %s", modestr);
Eric Andersenc470f442003-07-28 09:56:35 +000014116 }
Denys Vlasenko6283f982015-10-07 16:56:20 +020014117 if (!isdigit(modestr[0]))
14118 mask ^= 0777;
14119 umask(mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014120 }
14121 return 0;
14122}
14123
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014124static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014125ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014126{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014127 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014128}
14129
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014130/* ============ main() and helpers */
14131
14132/*
Denys Vlasenkof977e002020-02-20 16:54:29 +010014133 * This routine is called when an error or an interrupt occurs in an
14134 * interactive shell and control is returned to the main command loop
14135 * but prior to exitshell.
14136 */
14137static void
14138exitreset(void)
14139{
14140 /* from eval.c: */
14141 if (savestatus >= 0) {
14142 if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
14143 exitstatus = savestatus;
14144 savestatus = -1;
14145 }
14146 evalskip = 0;
14147 loopnest = 0;
14148
14149 /* from expand.c: */
14150 ifsfree();
14151
14152 /* from redir.c: */
14153 unwindredir(NULL);
14154}
14155
14156/*
14157 * This routine is called when an error or an interrupt occurs in an
14158 * interactive shell and control is returned to the main command loop.
14159 * (In dash, this function is auto-generated by build machinery).
14160 */
14161static void
14162reset(void)
14163{
14164 /* from input.c: */
14165 g_parsefile->left_in_buffer = 0;
14166 g_parsefile->left_in_line = 0; /* clear input buffer */
Denys Vlasenko51a471d2020-12-24 00:22:24 +010014167 g_parsefile->unget = 0;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014168 popallfiles();
14169
14170 /* from var.c: */
14171 unwindlocalvars(NULL);
14172}
14173
14174/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014175 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014176 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014177static void
14178exitshell(void)
14179{
14180 struct jmploc loc;
14181 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014182
Denys Vlasenkobede2152011-09-04 16:12:33 +020014183#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
Denys Vlasenko00eb23b2020-12-21 21:36:58 +010014184 save_history(line_input_state); /* may be NULL */
Denys Vlasenkobede2152011-09-04 16:12:33 +020014185#endif
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010014186 savestatus = exitstatus;
14187 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
14188 if (setjmp(loc.loc))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014189 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014190 exception_handler = &loc;
14191 p = trap[0];
14192 if (p) {
14193 trap[0] = NULL;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020014194 evalskip = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014195 evalstring(p, 0);
Denys Vlasenkof977e002020-02-20 16:54:29 +010014196 evalskip = SKIPFUNCDEF;
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014197 /*free(p); - we'll exit soon */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014198 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014199 out:
Denys Vlasenkof977e002020-02-20 16:54:29 +010014200 exitreset();
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014201 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14202 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
14203 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014204 setjobctl(0);
Denys Vlasenkocaee80c2016-10-25 20:49:53 +020014205 flush_stdout_stderr();
Denys Vlasenkof977e002020-02-20 16:54:29 +010014206 _exit(exitstatus);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014207 /* NOTREACHED */
14208}
14209
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014210/* Don't inline: conserve stack of caller from having our locals too */
14211static NOINLINE void
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014212init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014213{
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020014214 /* we will never free this */
14215 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denys Vlasenko0485b672017-08-14 19:46:56 +020014216 basepf.linno = 1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014217
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014218 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
Denys Vlasenko458c1f22016-10-27 23:51:19 +020014219 setsignal(SIGCHLD);
14220
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014221 {
14222 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014223 const char *p;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014224
14225 initvar();
14226 for (envp = environ; envp && *envp; envp++) {
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010014227/* Used to have
14228 * p = endofname(*envp);
14229 * if (p != *envp && *p == '=') {
14230 * here to weed out badly-named variables, but this breaks
14231 * scenarios where people do want them passed to children:
14232 * import os
14233 * os.environ["test-test"]="test"
14234 * if os.fork() == 0:
14235 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
14236 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14237 */
14238 if (strchr(*envp, '=')) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014239 setvareq(*envp, VEXPORT|VTEXTFIXED);
14240 }
14241 }
14242
Denys Vlasenko67dae152018-08-05 13:59:35 +020014243 setvareq((char*)defifsvar, VTEXTFIXED);
Denys Vlasenkoe627ac92016-09-30 14:36:59 +020014244 setvareq((char*)defoptindvar, VTEXTFIXED);
14245
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014246 setvar0("PPID", utoa(getppid()));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014247#if BASH_SHLVL_VAR
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014248 p = lookupvar("SHLVL");
Denys Vlasenko5680e982014-01-07 16:12:48 +010014249 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014250#endif
14251#if BASH_HOSTNAME_VAR
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014252 if (!lookupvar("HOSTNAME")) {
14253 struct utsname uts;
14254 uname(&uts);
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014255 setvar0("HOSTNAME", uts.nodename);
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014256 }
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014257#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014258 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014259 if (p) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014260 struct stat st1, st2;
Denys Vlasenkoef159702016-09-01 11:16:22 +020014261 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014262 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
14263 ) {
Denys Vlasenkoef159702016-09-01 11:16:22 +020014264 p = NULL;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014265 }
14266 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014267 setpwd(p, 0);
14268 }
14269}
14270
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014271
14272//usage:#define ash_trivial_usage
Denys Vlasenko4e039ba2021-01-04 03:50:38 +010014273//usage: "[-il] [-/+Cabefmnuvx] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]"
14274//////// comes from ^^^^^^^^^^optletters
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014275//usage:#define ash_full_usage "\n\n"
14276//usage: "Unix shell interpreter"
14277
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014278/*
14279 * Process the shell command line arguments.
14280 */
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014281static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000014282procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014283{
14284 int i;
14285 const char *xminusc;
14286 char **xargv;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014287 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014288
14289 xargv = argv;
Ron Yorston8767c122018-11-05 13:13:08 +000014290 login_sh = xargv[0] && xargv[0][0] == '-';
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014291#if NUM_SCRIPTS > 0
14292 if (minusc)
14293 goto setarg0;
14294#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014295 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000014296 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014297 xargv++;
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014298 argptr = xargv;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014299 for (i = 0; i < NOPTS; i++)
14300 optlist[i] = 2;
Denys Vlasenko897475a2019-06-01 16:35:09 +020014301 if (options(&login_sh)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000014302 /* it already printed err message */
14303 raise_exception(EXERROR);
14304 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014305 xargv = argptr;
14306 xminusc = minusc;
14307 if (*xargv == NULL) {
14308 if (xminusc)
14309 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
14310 sflag = 1;
14311 }
Denys Vlasenkof3634582019-06-03 12:21:04 +020014312 if (iflag == 2 /* no explicit -i given */
14313 && sflag == 1 /* -s given (or implied) */
14314 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
14315 && isatty(0) && isatty(1) /* we are on tty */
14316 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014317 iflag = 1;
Denys Vlasenkof3634582019-06-03 12:21:04 +020014318 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014319 if (mflag == 2)
14320 mflag = iflag;
Denys Vlasenko85158b62021-01-03 12:14:58 +010014321 /* Unset options which weren't explicitly set or unset */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014322 for (i = 0; i < NOPTS; i++)
Denys Vlasenko85158b62021-01-03 12:14:58 +010014323 optlist[i] &= 1; /* same effect as "if (optlist[i] == 2) optlist[i] = 0;" */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014324#if DEBUG == 2
14325 debug = 1;
14326#endif
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014327 /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014328 if (xminusc) {
14329 minusc = *xargv++;
14330 if (*xargv)
14331 goto setarg0;
14332 } else if (!sflag) {
14333 setinputfile(*xargv, 0);
14334 setarg0:
14335 arg0 = *xargv++;
14336 commandname = arg0;
14337 }
14338
14339 shellparam.p = xargv;
14340#if ENABLE_ASH_GETOPTS
14341 shellparam.optind = 1;
14342 shellparam.optoff = -1;
14343#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014344 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014345 while (*xargv) {
14346 shellparam.nparam++;
14347 xargv++;
14348 }
Denys Vlasenko31df5a32020-12-13 16:36:28 +010014349
14350 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
14351 * Try:
14352 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
14353 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
14354 * NB: must do it before setting up signals (in optschanged())
14355 * and reading .profile etc (after we return from here):
14356 */
14357 if (iflag)
14358 signal(SIGHUP, SIG_DFL);
14359
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014360 optschanged();
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014361
14362 return login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014363}
14364
14365/*
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014366 * Read /etc/profile, ~/.profile, $ENV.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014367 */
14368static void
14369read_profile(const char *name)
14370{
Denys Vlasenko46999802017-07-29 21:12:29 +020014371 name = expandstr(name, DQSYNTAX);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014372 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
14373 return;
Denys Vlasenko0840c912016-10-01 15:27:44 +020014374 cmdloop(0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014375 popfile();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014376}
14377
14378#if PROFILE
14379static short profile_buf[16384];
14380extern int etext();
14381#endif
14382
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014383/*
14384 * Main routine. We initialize things, parse the arguments, execute
14385 * profiles if we're a login shell, and then call cmdloop to execute
14386 * commands. The setjmp call sets up the location to jump to when an
14387 * exception occurs. When an exception occurs the variable "state"
14388 * is used to figure out how far we had gotten.
14389 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000014390int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Ron Yorston8767c122018-11-05 13:13:08 +000014391#if NUM_SCRIPTS > 0
14392int ash_main(int argc, char **argv)
14393#else
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014394int ash_main(int argc UNUSED_PARAM, char **argv)
Ron Yorston8767c122018-11-05 13:13:08 +000014395#endif
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014396/* note: 'argc' is used only if embedded scripts are enabled */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014397{
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014398 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014399 struct jmploc jmploc;
14400 struct stackmark smark;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014401 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014402
Denis Vlasenko01631112007-12-16 17:20:38 +000014403 /* Initialize global data */
14404 INIT_G_misc();
14405 INIT_G_memstack();
14406 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014407#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000014408 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014409#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014410 INIT_G_cmdtable();
14411
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014412#if PROFILE
14413 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
14414#endif
14415
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014416 state = 0;
14417 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014418 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014419 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014420
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014421 exitreset();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014422
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014423 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014424 s = state;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014425 if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014426 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020014427 }
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014428
14429 reset();
14430
Denys Vlasenkob563f622010-09-25 17:15:13 +020014431 if (e == EXINT) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020014432 newline_and_flush(stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020014433 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014434
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014435 popstackmark(&smark);
14436 FORCE_INT_ON; /* enable interrupts */
14437 if (s == 1)
14438 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014439 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014440 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014441 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014442 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014443 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014444 }
14445 exception_handler = &jmploc;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014446 rootpid = getpid();
14447
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014448 init();
14449 setstackmark(&smark);
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014450
14451#if NUM_SCRIPTS > 0
14452 if (argc < 0)
14453 /* Non-NULL minusc tells procargs that an embedded script is being run */
14454 minusc = get_script_content(-argc - 1);
14455#endif
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014456 login_sh = procargs(argv);
Denys Vlasenko474ed062016-10-30 18:30:29 +010014457#if DEBUG
14458 TRACE(("Shell args: "));
14459 trace_puts_args(argv);
14460#endif
Denis Vlasenko68404f12008-03-17 09:00:54 +000014461
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014462 if (login_sh) {
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014463 const char *hp;
14464
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014465 state = 1;
14466 read_profile("/etc/profile");
14467 state1:
14468 state = 2;
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014469 hp = lookupvar("HOME");
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014470 if (hp)
14471 read_profile("$HOME/.profile");
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014472 }
14473 state2:
14474 state = 3;
14475 if (
14476#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000014477 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014478#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000014479 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014480 ) {
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014481 const char *shinit = lookupvar("ENV");
14482 if (shinit != NULL && *shinit != '\0')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014483 read_profile(shinit);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014484 }
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014485 popstackmark(&smark);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014486 state3:
14487 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014488 if (minusc) {
14489 /* evalstring pushes parsefile stack.
14490 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000014491 * is one of stacked source fds.
14492 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenkof3634582019-06-03 12:21:04 +020014493
Denys Vlasenko79b3d422010-06-03 04:29:08 +020014494 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020014495 // ^^ not necessary since now we special-case fd 0
Denys Vlasenko035486c2017-07-31 04:09:19 +020014496 // in save_fd_on_redirect()
Denys Vlasenkof3634582019-06-03 12:21:04 +020014497
14498 // dash: evalstring(minusc, sflag ? 0 : EV_EXIT);
14499 // The above makes
14500 // ash -sc 'echo $-'
14501 // continue reading input from stdin after running 'echo'.
14502 // bash does not do this: it prints "hBcs" and exits.
14503 evalstring(minusc, EV_EXIT);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014504 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014505
14506 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020014507#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014508 if (line_input_state) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014509 const char *hp = lookupvar("HISTFILE");
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014510 if (!hp) {
14511 hp = lookupvar("HOME");
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014512 if (hp) {
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014513 INT_OFF;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014514 hp = concat_path_file(hp, ".ash_history");
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014515 setvar0("HISTFILE", hp);
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014516 free((char*)hp);
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014517 INT_ON;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014518 hp = lookupvar("HISTFILE");
14519 }
14520 }
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014521 if (hp)
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014522 line_input_state->hist_file = xstrdup(hp);
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020014523# if ENABLE_FEATURE_SH_HISTFILESIZE
14524 hp = lookupvar("HISTFILESIZE");
14525 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14526# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014527 }
14528#endif
14529 state4: /* XXX ??? - why isn't this before the "if" statement */
14530 cmdloop(1);
14531 }
14532#if PROFILE
14533 monitor(0);
14534#endif
14535#ifdef GPROF
14536 {
14537 extern void _mcleanup(void);
14538 _mcleanup();
14539 }
14540#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020014541 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014542 exitshell();
14543 /* NOTREACHED */
14544}
14545
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014546
Eric Andersendf82f612001-06-28 07:46:40 +000014547/*-
14548 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000014549 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000014550 *
14551 * This code is derived from software contributed to Berkeley by
14552 * Kenneth Almquist.
14553 *
14554 * Redistribution and use in source and binary forms, with or without
14555 * modification, are permitted provided that the following conditions
14556 * are met:
14557 * 1. Redistributions of source code must retain the above copyright
14558 * notice, this list of conditions and the following disclaimer.
14559 * 2. Redistributions in binary form must reproduce the above copyright
14560 * notice, this list of conditions and the following disclaimer in the
14561 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000014562 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000014563 * may be used to endorse or promote products derived from this software
14564 * without specific prior written permission.
14565 *
Denys Vlasenko95f79532017-08-02 14:26:33 +020014566 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
Eric Andersendf82f612001-06-28 07:46:40 +000014567 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14568 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14569 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
14570 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14571 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14572 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14573 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
14574 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
14575 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
14576 * SUCH DAMAGE.
14577 */