blob: 99fdbce7bdb49268d2fa7b995f75c886756b490e [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:
Shawn Landden58598eb2022-08-27 19:56:21 +0200137//config:config ASH_SLEEP
138//config: bool "sleep builtin"
139//config: default y
140//config: depends on SHELL_ASH
141//config:
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200142//config:config ASH_HELP
143//config: bool "help builtin"
144//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200145//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +0100146//config:
147//config:config ASH_GETOPTS
148//config: bool "getopts builtin"
149//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200150//config: depends on SHELL_ASH
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200151//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200152//config:config ASH_CMDCMD
Denys Vlasenkof5604222017-01-10 14:58:54 +0100153//config: bool "command builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200154//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200155//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200156//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200157//config: Enable support for the 'command' builtin, which allows
158//config: you to run the specified command or builtin,
159//config: even when there is a function with the same name.
Kang-Che Sung6cd02942017-01-06 17:02:03 +0100160//config:
161//config:endif # ash options
Denys Vlasenko771f1992010-07-16 14:31:34 +0200162
Denys Vlasenko20704f02011-03-23 17:59:27 +0100163//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
Denys Vlasenko205d48e2017-01-29 14:57:33 +0100164// APPLET_ODDNAME:name main location suid_type help
165//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko0b883582016-12-23 16:49:07 +0100166//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko20704f02011-03-23 17:59:27 +0100167
Denys Vlasenko67e15292020-06-24 13:39:13 +0200168//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko20704f02011-03-23 17:59:27 +0100169//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
170
Denys Vlasenko67047462016-12-22 15:21:58 +0100171/*
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100172 * DEBUG=1 to compile in debugging ('set -o debug' turns on)
173 * DEBUG=2 to compile in and turn on debugging.
174 * When debugging is on ("set -o debug" was executed, or DEBUG=2),
175 * debugging info is written to ./trace, quit signal generates core dump.
Denys Vlasenko67047462016-12-22 15:21:58 +0100176 */
177#define DEBUG 0
178/* Tweak debug output verbosity here */
179#define DEBUG_TIME 0
180#define DEBUG_PID 1
181#define DEBUG_SIG 1
182#define DEBUG_INTONOFF 0
183
184#define PROFILE 0
185
186#define JOBS ENABLE_ASH_JOB_CONTROL
187
Denys Vlasenko67047462016-12-22 15:21:58 +0100188#include <fnmatch.h>
189#include <sys/times.h>
190#include <sys/utsname.h> /* for setting $HOSTNAME */
Denys Vlasenko67047462016-12-22 15:21:58 +0100191#include "busybox.h" /* for applet_names */
Ron Yorston71df2d32018-11-27 14:34:25 +0000192#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +0100193# include "embedded_scripts.h"
194#else
195# define NUM_SCRIPTS 0
196#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100197
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100198/* So far, all bash compat is controlled by one config option */
199/* Separate defines document which part of code implements what */
200/* function keyword */
201#define BASH_FUNCTION ENABLE_ASH_BASH_COMPAT
202#define IF_BASH_FUNCTION IF_ASH_BASH_COMPAT
203/* &>file */
204#define BASH_REDIR_OUTPUT ENABLE_ASH_BASH_COMPAT
205#define IF_BASH_REDIR_OUTPUT IF_ASH_BASH_COMPAT
206/* $'...' */
207#define BASH_DOLLAR_SQUOTE ENABLE_ASH_BASH_COMPAT
208#define IF_BASH_DOLLAR_SQUOTE IF_ASH_BASH_COMPAT
209#define BASH_PATTERN_SUBST ENABLE_ASH_BASH_COMPAT
210#define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT
211#define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT
212#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200213/* BASH_TEST2: [[ EXPR ]]
214 * Status of [[ support:
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100215 * && and || work as they should
216 * = is glob match operator, not equality operator: STR = GLOB
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100217 * == same as =
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100218 * =~ is regex match operator: STR =~ REGEX
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200219 * TODO:
220 * singleword+noglob expansion:
221 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
Denys Vlasenko89e9d552018-04-11 01:15:33 +0200222 * [[ /bin/n* ]]; echo 0:$?
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200223 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100224 * ( ) < > should not have special meaning (IOW: should not require quoting)
225 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200226 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100227#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
228#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
229#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
230#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
Ron Yorston1d371862019-04-15 10:52:05 +0100231#define BASH_EPOCH_VARS ENABLE_ASH_BASH_COMPAT
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100232#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +0200233#define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT
Johannes Schindelin3bef5d82017-08-08 16:46:39 +0200234#define BASH_READ_D ENABLE_ASH_BASH_COMPAT
235#define IF_BASH_READ_D IF_ASH_BASH_COMPAT
Ron Yorstone48559e2019-03-31 09:27:09 +0100236#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT
Ron Yorstona1b0d382020-07-23 08:32:27 +0100237/* <(...) and >(...) */
238#if HAVE_DEV_FD
239# define BASH_PROCESS_SUBST ENABLE_ASH_BASH_COMPAT
240# define IF_BASH_PROCESS_SUBST IF_ASH_BASH_COMPAT
241#else
242# define BASH_PROCESS_SUBST 0
243# define IF_BASH_PROCESS_SUBST(...)
244#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100245
Denys Vlasenko67047462016-12-22 15:21:58 +0100246#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
247/* Bionic at least up to version 24 has no glob() */
248# undef ENABLE_ASH_INTERNAL_GLOB
249# define ENABLE_ASH_INTERNAL_GLOB 1
250#endif
251
252#if !ENABLE_ASH_INTERNAL_GLOB && defined(__UCLIBC__)
253# error uClibc glob() is buggy, use ASH_INTERNAL_GLOB.
254# error The bug is: for "$PWD"/<pattern> ash will escape e.g. dashes in "$PWD"
255# error with backslash, even ones which do not need to be: "/a-b" -> "/a\-b"
256# error glob() should unbackslash them and match. uClibc does not unbackslash,
257# error fails to match dirname, subsequently not expanding <pattern> in it.
258// Testcase:
259// if (glob("/etc/polkit\\-1", 0, NULL, &pglob)) - this returns 0 on uclibc, no bug
260// if (glob("/etc/polkit\\-1/*", 0, NULL, &pglob)) printf("uclibc bug!\n");
261#endif
262
263#if !ENABLE_ASH_INTERNAL_GLOB
264# include <glob.h>
265#endif
266
267#include "unicode.h"
268#include "shell_common.h"
Denys Vlasenko0b883582016-12-23 16:49:07 +0100269#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko67047462016-12-22 15:21:58 +0100270# include "math.h"
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200271#else
272typedef long arith_t;
273# define ARITH_FMT "%ld"
Denys Vlasenko67047462016-12-22 15:21:58 +0100274#endif
275#if ENABLE_ASH_RANDOM_SUPPORT
276# include "random.h"
277#else
278# define CLEAR_RANDOM_T(rnd) ((void)0)
279#endif
280
281#include "NUM_APPLETS.h"
282#if NUM_APPLETS == 1
283/* STANDALONE does not make sense, and won't compile */
284# undef CONFIG_FEATURE_SH_STANDALONE
285# undef ENABLE_FEATURE_SH_STANDALONE
286# undef IF_FEATURE_SH_STANDALONE
287# undef IF_NOT_FEATURE_SH_STANDALONE
288# define ENABLE_FEATURE_SH_STANDALONE 0
289# define IF_FEATURE_SH_STANDALONE(...)
290# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
291#endif
292
Denys Vlasenko9acd63c2018-03-28 18:35:07 +0200293#ifndef F_DUPFD_CLOEXEC
294# define F_DUPFD_CLOEXEC F_DUPFD
295#endif
Denys Vlasenko60fb98e2018-03-30 22:15:14 +0200296#ifndef O_CLOEXEC
297# define O_CLOEXEC 0
298#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100299#ifndef PIPE_BUF
300# define PIPE_BUF 4096 /* amount of buffering in a pipe */
301#endif
302
Denys Vlasenko48cb9832021-09-08 09:52:04 +0200303#ifndef unlikely
304# define unlikely(cond) (cond)
305#endif
306
Denys Vlasenko67047462016-12-22 15:21:58 +0100307#if !BB_MMU
308# error "Do not even bother, ash will not run on NOMMU machine"
309#endif
310
Denis Vlasenko01631112007-12-16 17:20:38 +0000311/* ============ Hash table sizes. Configurable. */
312
313#define VTABSIZE 39
314#define ATABSIZE 39
315#define CMDTABLESIZE 31 /* should be prime */
316
317
Denis Vlasenkob012b102007-02-19 22:43:01 +0000318/* ============ Shell options */
319
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100320/* If you add/change options hare, update --help text too */
Denys Vlasenkoca466f32022-02-06 19:53:10 +0100321static const char *const optletters_optnames[] ALIGN_PTR = {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000322 "e" "errexit",
323 "f" "noglob",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100324/* bash has '-o ignoreeof', but no short synonym -I for it */
325/* (in bash, set -I disables invisible variables (what's that?)) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000326 "I" "ignoreeof",
Denys Vlasenkof3634582019-06-03 12:21:04 +0200327/* The below allowed this invocation:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200328 * ash -c 'set -i; echo $-; sleep 5; echo $-'
329 * to be ^C-ed and get to interactive ash prompt.
Denys Vlasenkof3634582019-06-03 12:21:04 +0200330 * bash does not support such "set -i".
331 * In our code, this is denoted by empty long name:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200332 */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200333 "i" "",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100334/* (removing "i" altogether would remove it from "$-", not good) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000335 "m" "monitor",
336 "n" "noexec",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100337/* Ditto: bash has no "set -s", "set -c" */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200338 "s" "",
339 "c" "",
Denis Vlasenkob012b102007-02-19 22:43:01 +0000340 "x" "xtrace",
341 "v" "verbose",
342 "C" "noclobber",
343 "a" "allexport",
344 "b" "notify",
345 "u" "nounset",
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200346 "E" "errtrace",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100347 "\0" "vi"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100348#if BASH_PIPEFAIL
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100349 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100350#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000351#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000352 ,"\0" "nolog"
353 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000354#endif
355};
Denys Vlasenko897475a2019-06-01 16:35:09 +0200356//bash 4.4.23 also has these opts (with these defaults):
357//braceexpand on
358//emacs on
359//errtrace off
360//functrace off
361//hashall on
362//histexpand off
363//history on
364//interactive-comments on
365//keyword off
366//onecmd off
367//physical off
368//posix off
369//privileged off
Denis Vlasenkob012b102007-02-19 22:43:01 +0000370
Denys Vlasenko285ad152009-12-04 23:02:27 +0100371#define optletters(n) optletters_optnames[n][0]
372#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000373
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000374enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000375
Eric Andersenc470f442003-07-28 09:56:35 +0000376
Denis Vlasenkob012b102007-02-19 22:43:01 +0000377/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000378
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200379#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000380
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000381/*
Eric Andersenc470f442003-07-28 09:56:35 +0000382 * We enclose jmp_buf in a structure so that we can declare pointers to
383 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000384 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000385 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000386 * exception handlers, the user should save the value of handler on entry
387 * to an inner scope, set handler to point to a jmploc structure for the
388 * inner scope, and restore handler on exit from the scope.
389 */
Eric Andersenc470f442003-07-28 09:56:35 +0000390struct jmploc {
391 jmp_buf loc;
392};
Denis Vlasenko01631112007-12-16 17:20:38 +0000393
394struct globals_misc {
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200395 uint8_t exitstatus; /* exit status of last command */
396 uint8_t back_exitstatus;/* exit status of backquoted command */
397 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denys Vlasenkoeb607772021-09-09 16:26:41 +0200398 smallint inps4; /* Prevent PS4 nesting. */
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100399 int savestatus; /* exit status of last command outside traps */
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200400 int rootpid; /* pid of main shell */
Denis Vlasenko01631112007-12-16 17:20:38 +0000401 /* shell level: 0 for the main shell, 1 for its children, and so on */
402 int shlvl;
403#define rootshell (!shlvl)
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100404 int errlinno;
405
Denis Vlasenko01631112007-12-16 17:20:38 +0000406 char *minusc; /* argument to -c option */
407
408 char *curdir; // = nullstr; /* current working directory */
409 char *physdir; // = nullstr; /* physical working directory */
410
411 char *arg0; /* value of $0 */
412
413 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000414
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200415 volatile int suppress_int; /* counter */
416 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200417 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
Denys Vlasenko8f7b0242016-10-28 17:16:11 +0200418 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100419 smallint exception_type; /* kind of exception: */
Eric Andersenc470f442003-07-28 09:56:35 +0000420#define EXINT 0 /* SIGINT received */
421#define EXERROR 1 /* a generic error */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100422#define EXEND 3 /* exit the shell */
423#define EXEXIT 4 /* exit the shell via exitcmd */
Eric Andersen2870d962001-07-02 17:27:21 +0000424
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000425 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000426
427 char optlist[NOPTS];
428#define eflag optlist[0]
429#define fflag optlist[1]
430#define Iflag optlist[2]
431#define iflag optlist[3]
432#define mflag optlist[4]
433#define nflag optlist[5]
434#define sflag optlist[6]
Denys Vlasenkof3634582019-06-03 12:21:04 +0200435#define cflag optlist[7]
436#define xflag optlist[8]
437#define vflag optlist[9]
438#define Cflag optlist[10]
439#define aflag optlist[11]
440#define bflag optlist[12]
441#define uflag optlist[13]
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200442#define Eflag optlist[14]
443#define viflag optlist[15]
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100444#if BASH_PIPEFAIL
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200445# define pipefail optlist[16]
Michael Abbott359da5e2009-12-04 23:03:29 +0100446#else
447# define pipefail 0
448#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000449#if DEBUG
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200450# define nolog optlist[16 + BASH_PIPEFAIL]
451# define debug optlist[17 + BASH_PIPEFAIL]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000452#endif
453
454 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000455 /*
456 * Sigmode records the current value of the signal handlers for the various
457 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000458 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000459 */
460 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000461#define S_DFL 1 /* default signal handling (SIG_DFL) */
462#define S_CATCH 2 /* signal is caught */
463#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denys Vlasenko0f14f412017-08-06 20:06:19 +0200464#define S_HARD_IGN 4 /* signal is ignored permanently (it was SIG_IGN on entry to shell) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000465
Denis Vlasenko01631112007-12-16 17:20:38 +0000466 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000467 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200468 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200469 char *trap[NSIG + 1];
470/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
471#define NTRAP_ERR NSIG
472#define NTRAP_LAST NSIG
473
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200474 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000475
476 /* Rarely referenced stuff */
477#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200478 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000479#endif
480 pid_t backgndpid; /* pid of last background process */
Denis Vlasenko01631112007-12-16 17:20:38 +0000481};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100482extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000483#define G_misc (*ash_ptr_to_globals_misc)
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200484#define exitstatus (G_misc.exitstatus )
485#define back_exitstatus (G_misc.back_exitstatus )
486#define job_warning (G_misc.job_warning)
Denys Vlasenkoeb607772021-09-09 16:26:41 +0200487#define inps4 (G_misc.inps4 )
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100488#define savestatus (G_misc.savestatus )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000489#define rootpid (G_misc.rootpid )
490#define shlvl (G_misc.shlvl )
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100491#define errlinno (G_misc.errlinno )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000492#define minusc (G_misc.minusc )
493#define curdir (G_misc.curdir )
494#define physdir (G_misc.physdir )
495#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000496#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000497#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200498#define suppress_int (G_misc.suppress_int )
499#define pending_int (G_misc.pending_int )
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200500#define got_sigchld (G_misc.got_sigchld )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200501#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000502#define nullstr (G_misc.nullstr )
503#define optlist (G_misc.optlist )
504#define sigmode (G_misc.sigmode )
505#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200506#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000507#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200508#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200509#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000510#define backgndpid (G_misc.backgndpid )
Denis Vlasenko01631112007-12-16 17:20:38 +0000511#define INIT_G_misc() do { \
YU Jincheng5156b242021-10-10 02:19:51 +0800512 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100513 savestatus = -1; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000514 curdir = nullstr; \
515 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200516 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000517} while (0)
518
519
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000520/* ============ DEBUG */
521#if DEBUG
522static void trace_printf(const char *fmt, ...);
523static void trace_vprintf(const char *fmt, va_list va);
524# define TRACE(param) trace_printf param
525# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000526# define close(fd) do { \
527 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000528 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200529 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000530 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000531} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000532#else
533# define TRACE(param)
534# define TRACEV(param)
535#endif
536
537
Denis Vlasenko559691a2008-10-05 18:39:31 +0000538/* ============ Utility functions */
Denys Vlasenko1961aea2013-02-26 00:36:53 +0100539#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
540#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
541
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200542static int
543isdigit_str9(const char *str)
Denis Vlasenko559691a2008-10-05 18:39:31 +0000544{
545 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
546 while (--maxlen && isdigit(*str))
547 str++;
548 return (*str == '\0');
549}
Denis Vlasenko01631112007-12-16 17:20:38 +0000550
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200551static const char *
552var_end(const char *var)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200553{
554 while (*var)
555 if (*var++ == '=')
556 break;
557 return var;
558}
559
Denis Vlasenko559691a2008-10-05 18:39:31 +0000560
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100561/* ============ Parser data */
562
563/*
564 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
565 */
566struct strlist {
567 struct strlist *next;
568 char *text;
569};
570
571struct alias;
572
573struct strpush {
574 struct strpush *prev; /* preceding string on stack */
575 char *prev_string;
576 int prev_left_in_line;
577#if ENABLE_ASH_ALIAS
578 struct alias *ap; /* if push was associated with an alias */
579#endif
580 char *string; /* remember the string since it may change */
581
Denys Vlasenko48cb9832021-09-08 09:52:04 +0200582 /* Delay freeing so we can stop nested aliases. */
583 struct strpush *spfree;
584
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100585 /* Remember last two characters for pungetc. */
586 int lastc[2];
587
588 /* Number of outstanding calls to pungetc. */
589 int unget;
590};
591
592/*
593 * The parsefile structure pointed to by the global variable parsefile
594 * contains information about the current file being read.
595 */
596struct parsefile {
597 struct parsefile *prev; /* preceding file on stack */
598 int linno; /* current line */
599 int pf_fd; /* file descriptor (or -1 if string) */
600 int left_in_line; /* number of chars left in this line */
601 int left_in_buffer; /* number of chars left in this buffer past the line */
602 char *next_to_pgetc; /* next char in buffer */
603 char *buf; /* input buffer */
604 struct strpush *strpush; /* for pushing strings at this level */
605 struct strpush basestrpush; /* so pushing one is fast */
606
Denys Vlasenko48cb9832021-09-08 09:52:04 +0200607 /* Delay freeing so we can stop nested aliases. */
608 struct strpush *spfree;
609
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100610 /* Remember last two characters for pungetc. */
611 int lastc[2];
612
613 /* Number of outstanding calls to pungetc. */
614 int unget;
615};
616
617static struct parsefile basepf; /* top level input file */
618static struct parsefile *g_parsefile = &basepf; /* current input file */
619static char *commandname; /* currently executing command */
620
621
Denis Vlasenko559691a2008-10-05 18:39:31 +0000622/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100623
624static void exitshell(void) NORETURN;
625
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000626/*
Eric Andersen2870d962001-07-02 17:27:21 +0000627 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000628 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000629 * much more efficient and portable. (But hacking the kernel is so much
630 * more fun than worrying about efficiency and portability. :-))
631 */
Denys Vlasenko06b11492016-11-04 16:43:18 +0100632#if DEBUG_INTONOFF
633# define INT_OFF do { \
634 TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200635 suppress_int++; \
Denys Vlasenkode892052016-10-02 01:49:13 +0200636 barrier(); \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000637} while (0)
Denys Vlasenko06b11492016-11-04 16:43:18 +0100638#else
639# define INT_OFF do { \
640 suppress_int++; \
641 barrier(); \
642} while (0)
643#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000644
645/*
646 * Called to raise an exception. Since C doesn't include exceptions, we
647 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000648 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000649 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000650static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000651static void
652raise_exception(int e)
653{
654#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000655 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000656 abort();
657#endif
658 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000659 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000660 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000661}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000662#if DEBUG
663#define raise_exception(e) do { \
664 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
665 raise_exception(e); \
666} while (0)
667#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000668
669/*
Denys Vlasenkof37e1152016-10-07 03:17:28 +0200670 * Called when a SIGINT is received. (If the user specifies
Denis Vlasenkob012b102007-02-19 22:43:01 +0000671 * that SIGINT is to be trapped or ignored using the trap builtin, then
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100672 * this routine is not called.) suppress_int is nonzero when interrupts
Denis Vlasenkob012b102007-02-19 22:43:01 +0000673 * are held using the INT_OFF macro. (The test for iflag is just
674 * defensive programming.)
675 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000676static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000677static void
678raise_interrupt(void)
679{
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200680 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000681 /* Signal is not automatically unmasked after it is raised,
682 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000683 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200684 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000685
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200686 if (!(rootshell && iflag)) {
687 /* Kill ourself with SIGINT */
688 signal(SIGINT, SIG_DFL);
689 raise(SIGINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000690 }
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200691 /* bash: ^C even on empty command line sets $? */
692 exitstatus = SIGINT + 128;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200693 raise_exception(EXINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000694 /* NOTREACHED */
695}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000696#if DEBUG
697#define raise_interrupt() do { \
698 TRACE(("raising interrupt on line %d\n", __LINE__)); \
699 raise_interrupt(); \
700} while (0)
701#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000702
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100703static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000704int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000705{
Denys Vlasenkode892052016-10-02 01:49:13 +0200706 barrier();
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100707 if (--suppress_int == 0 && pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000708 raise_interrupt();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000709}
Denys Vlasenko06b11492016-11-04 16:43:18 +0100710#if DEBUG_INTONOFF
711# define INT_ON do { \
712 TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \
713 int_on(); \
714} while (0)
715#else
716# define INT_ON int_on()
717#endif
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100718static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000719force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000720{
Denys Vlasenkode892052016-10-02 01:49:13 +0200721 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200722 suppress_int = 0;
723 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000724 raise_interrupt();
725}
726#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000727
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200728#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000729
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000730#define RESTORE_INT(v) do { \
Denys Vlasenkode892052016-10-02 01:49:13 +0200731 barrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200732 suppress_int = (v); \
733 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000734 raise_interrupt(); \
735} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000736
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000737
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000738/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000739
Eric Andersenc470f442003-07-28 09:56:35 +0000740static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000741outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000742{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000743 INT_OFF;
744 fputs(p, file);
745 INT_ON;
746}
747
748static void
749flush_stdout_stderr(void)
750{
751 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100752 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000753 INT_ON;
754}
755
Denys Vlasenko9c541002015-10-07 15:44:36 +0200756/* Was called outcslow(c,FILE*), but c was always '\n' */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000757static void
Denys Vlasenko9c541002015-10-07 15:44:36 +0200758newline_and_flush(FILE *dest)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000759{
760 INT_OFF;
Denys Vlasenko9c541002015-10-07 15:44:36 +0200761 putc('\n', dest);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000762 fflush(dest);
763 INT_ON;
764}
765
766static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
767static int
768out1fmt(const char *fmt, ...)
769{
770 va_list ap;
771 int r;
772
773 INT_OFF;
774 va_start(ap, fmt);
775 r = vprintf(fmt, ap);
776 va_end(ap);
777 INT_ON;
778 return r;
779}
780
781static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
782static int
783fmtstr(char *outbuf, size_t length, const char *fmt, ...)
784{
785 va_list ap;
786 int ret;
787
Denis Vlasenkob012b102007-02-19 22:43:01 +0000788 INT_OFF;
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200789 va_start(ap, fmt);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000790 ret = vsnprintf(outbuf, length, fmt, ap);
791 va_end(ap);
792 INT_ON;
Denys Vlasenko3f7fb2c2020-02-16 18:06:20 +0100793 return ret > (int)length ? length : ret;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000794}
795
796static void
797out1str(const char *p)
798{
799 outstr(p, stdout);
800}
801
802static void
803out2str(const char *p)
804{
805 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100806 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000807}
808
809
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000810/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000811
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000812/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100813#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200814#define CTLESC ((unsigned char)'\201') /* escape next character */
815#define CTLVAR ((unsigned char)'\202') /* variable defn */
816#define CTLENDVAR ((unsigned char)'\203')
817#define CTLBACKQ ((unsigned char)'\204')
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200818#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
819#define CTLENDARI ((unsigned char)'\207')
820#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100821#define CTL_LAST CTLQUOTEMARK
Ron Yorstona1b0d382020-07-23 08:32:27 +0100822#if BASH_PROCESS_SUBST
823# define CTLTOPROC ((unsigned char)'\211')
824# define CTLFROMPROC ((unsigned char)'\212')
825# undef CTL_LAST
826# define CTL_LAST CTLFROMPROC
827#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000828
829/* variable substitution byte (follows CTLVAR) */
830#define VSTYPE 0x0f /* type of variable substitution */
831#define VSNUL 0x10 /* colon--treat the empty string as unset */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000832
833/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000834#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
835#define VSMINUS 0x2 /* ${var-text} */
836#define VSPLUS 0x3 /* ${var+text} */
837#define VSQUESTION 0x4 /* ${var?message} */
838#define VSASSIGN 0x5 /* ${var=text} */
839#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
840#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
841#define VSTRIMLEFT 0x8 /* ${var#pattern} */
842#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
843#define VSLENGTH 0xa /* ${#var} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100844#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000845#define VSSUBSTR 0xc /* ${var:position:length} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100846#endif
847#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000848#define VSREPLACE 0xd /* ${var/pattern/replacement} */
849#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
850#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000851
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000852static const char dolatstr[] ALIGN1 = {
Ron Yorston549deab2015-05-18 09:57:51 +0200853 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000854};
Ron Yorston549deab2015-05-18 09:57:51 +0200855#define DOLATSTRLEN 6
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000856
Denis Vlasenko559691a2008-10-05 18:39:31 +0000857#define NCMD 0
858#define NPIPE 1
859#define NREDIR 2
860#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000861#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000862#define NAND 5
863#define NOR 6
864#define NSEMI 7
865#define NIF 8
866#define NWHILE 9
867#define NUNTIL 10
868#define NFOR 11
869#define NCASE 12
870#define NCLIST 13
871#define NDEFUN 14
872#define NARG 15
873#define NTO 16
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100874#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +0000875#define NTO2 17
876#endif
877#define NCLOBBER 18
878#define NFROM 19
879#define NFROMTO 20
880#define NAPPEND 21
881#define NTOFD 22
882#define NFROMFD 23
883#define NHERE 24
884#define NXHERE 25
885#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000886#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000887
888union node;
889
890struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000891 smallint type; /* Nxxxx */
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100892 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000893 union node *assign;
894 union node *args;
895 union node *redirect;
896};
897
898struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000899 smallint type;
900 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000901 struct nodelist *cmdlist;
902};
903
904struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000905 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100906 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000907 union node *n;
908 union node *redirect;
909};
910
911struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000912 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000913 union node *ch1;
914 union node *ch2;
915};
916
917struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000918 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000919 union node *test;
920 union node *ifpart;
921 union node *elsepart;
922};
923
924struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000925 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100926 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000927 union node *args;
928 union node *body;
929 char *var;
930};
931
932struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000933 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100934 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000935 union node *expr;
936 union node *cases;
937};
938
939struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000940 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000941 union node *next;
942 union node *pattern;
943 union node *body;
944};
945
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100946struct ndefun {
947 smallint type;
948 int linno;
949 char *text;
950 union node *body;
951};
952
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000953struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000954 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000955 union node *next;
956 char *text;
957 struct nodelist *backquote;
958};
959
Denis Vlasenko559691a2008-10-05 18:39:31 +0000960/* nfile and ndup layout must match!
961 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
962 * that it is actually NTO2 (>&file), and change its type.
963 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000964struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000965 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000966 union node *next;
967 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000968 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000969 union node *fname;
970 char *expfname;
971};
972
973struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000974 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000975 union node *next;
976 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000977 int dupfd;
978 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000979 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000980};
981
982struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000983 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000984 union node *next;
985 int fd;
986 union node *doc;
987};
988
989struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000990 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000991 union node *com;
992};
993
994union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000995 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000996 struct ncmd ncmd;
997 struct npipe npipe;
998 struct nredir nredir;
999 struct nbinary nbinary;
1000 struct nif nif;
1001 struct nfor nfor;
1002 struct ncase ncase;
1003 struct nclist nclist;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001004 struct ndefun ndefun;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001005 struct narg narg;
1006 struct nfile nfile;
1007 struct ndup ndup;
1008 struct nhere nhere;
1009 struct nnot nnot;
1010};
1011
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001012/*
1013 * NODE_EOF is returned by parsecmd when it encounters an end of file.
1014 * It must be distinct from NULL.
1015 */
1016#define NODE_EOF ((union node *) -1L)
1017
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001018struct nodelist {
1019 struct nodelist *next;
1020 union node *n;
1021};
1022
1023struct funcnode {
1024 int count;
1025 union node n;
1026};
1027
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001028/*
1029 * Free a parse tree.
1030 */
1031static void
1032freefunc(struct funcnode *f)
1033{
1034 if (f && --f->count < 0)
1035 free(f);
1036}
1037
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001038
1039/* ============ Debugging output */
1040
1041#if DEBUG
1042
1043static FILE *tracefile;
1044
1045static void
1046trace_printf(const char *fmt, ...)
1047{
1048 va_list va;
1049
1050 if (debug != 1)
1051 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00001052 if (DEBUG_TIME)
1053 fprintf(tracefile, "%u ", (int) time(NULL));
1054 if (DEBUG_PID)
1055 fprintf(tracefile, "[%u] ", (int) getpid());
1056 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001057 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001058 va_start(va, fmt);
1059 vfprintf(tracefile, fmt, va);
1060 va_end(va);
1061}
1062
1063static void
1064trace_vprintf(const char *fmt, va_list va)
1065{
1066 if (debug != 1)
1067 return;
1068 vfprintf(tracefile, fmt, va);
Denys Vlasenko474ed062016-10-30 18:30:29 +01001069 fprintf(tracefile, "\n");
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001070}
1071
1072static void
1073trace_puts(const char *s)
1074{
1075 if (debug != 1)
1076 return;
1077 fputs(s, tracefile);
1078}
1079
1080static void
1081trace_puts_quoted(char *s)
1082{
1083 char *p;
1084 char c;
1085
1086 if (debug != 1)
1087 return;
1088 putc('"', tracefile);
1089 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001090 switch ((unsigned char)*p) {
1091 case '\n': c = 'n'; goto backslash;
1092 case '\t': c = 't'; goto backslash;
1093 case '\r': c = 'r'; goto backslash;
1094 case '\"': c = '\"'; goto backslash;
1095 case '\\': c = '\\'; goto backslash;
1096 case CTLESC: c = 'e'; goto backslash;
1097 case CTLVAR: c = 'v'; goto backslash;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001098 case CTLBACKQ: c = 'q'; goto backslash;
Ron Yorstona1b0d382020-07-23 08:32:27 +01001099#if BASH_PROCESS_SUBST
1100 case CTLTOPROC: c = 'p'; goto backslash;
1101 case CTLFROMPROC: c = 'P'; goto backslash;
1102#endif
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001103 backslash:
1104 putc('\\', tracefile);
1105 putc(c, tracefile);
1106 break;
1107 default:
1108 if (*p >= ' ' && *p <= '~')
1109 putc(*p, tracefile);
1110 else {
1111 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +01001112 putc((*p >> 6) & 03, tracefile);
1113 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001114 putc(*p & 07, tracefile);
1115 }
1116 break;
1117 }
1118 }
1119 putc('"', tracefile);
1120}
1121
1122static void
1123trace_puts_args(char **ap)
1124{
1125 if (debug != 1)
1126 return;
1127 if (!*ap)
1128 return;
1129 while (1) {
1130 trace_puts_quoted(*ap);
1131 if (!*++ap) {
1132 putc('\n', tracefile);
1133 break;
1134 }
1135 putc(' ', tracefile);
1136 }
1137}
1138
1139static void
1140opentrace(void)
1141{
1142 char s[100];
1143#ifdef O_APPEND
1144 int flags;
1145#endif
1146
1147 if (debug != 1) {
1148 if (tracefile)
1149 fflush(tracefile);
1150 /* leave open because libedit might be using it */
1151 return;
1152 }
1153 strcpy(s, "./trace");
1154 if (tracefile) {
1155 if (!freopen(s, "a", tracefile)) {
1156 fprintf(stderr, "Can't re-open %s\n", s);
1157 debug = 0;
1158 return;
1159 }
1160 } else {
1161 tracefile = fopen(s, "a");
1162 if (tracefile == NULL) {
1163 fprintf(stderr, "Can't open %s\n", s);
1164 debug = 0;
1165 return;
1166 }
1167 }
1168#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001169 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001170 if (flags >= 0)
1171 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
1172#endif
1173 setlinebuf(tracefile);
1174 fputs("\nTracing started.\n", tracefile);
1175}
1176
1177static void
1178indent(int amount, char *pfx, FILE *fp)
1179{
1180 int i;
1181
1182 for (i = 0; i < amount; i++) {
1183 if (pfx && i == amount - 1)
1184 fputs(pfx, fp);
1185 putc('\t', fp);
1186 }
1187}
1188
1189/* little circular references here... */
1190static void shtree(union node *n, int ind, char *pfx, FILE *fp);
1191
1192static void
1193sharg(union node *arg, FILE *fp)
1194{
1195 char *p;
1196 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001197 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001198
1199 if (arg->type != NARG) {
1200 out1fmt("<node type %d>\n", arg->type);
1201 abort();
1202 }
1203 bqlist = arg->narg.backquote;
1204 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001205 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001206 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -07001207 p++;
1208 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001209 break;
1210 case CTLVAR:
1211 putc('$', fp);
1212 putc('{', fp);
1213 subtype = *++p;
1214 if (subtype == VSLENGTH)
1215 putc('#', fp);
1216
Dan Fandrich77d48722010-09-07 23:38:28 -07001217 while (*p != '=') {
1218 putc(*p, fp);
1219 p++;
1220 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001221
1222 if (subtype & VSNUL)
1223 putc(':', fp);
1224
1225 switch (subtype & VSTYPE) {
1226 case VSNORMAL:
1227 putc('}', fp);
1228 break;
1229 case VSMINUS:
1230 putc('-', fp);
1231 break;
1232 case VSPLUS:
1233 putc('+', fp);
1234 break;
1235 case VSQUESTION:
1236 putc('?', fp);
1237 break;
1238 case VSASSIGN:
1239 putc('=', fp);
1240 break;
1241 case VSTRIMLEFT:
1242 putc('#', fp);
1243 break;
1244 case VSTRIMLEFTMAX:
1245 putc('#', fp);
1246 putc('#', fp);
1247 break;
1248 case VSTRIMRIGHT:
1249 putc('%', fp);
1250 break;
1251 case VSTRIMRIGHTMAX:
1252 putc('%', fp);
1253 putc('%', fp);
1254 break;
1255 case VSLENGTH:
1256 break;
1257 default:
1258 out1fmt("<subtype %d>", subtype);
1259 }
1260 break;
1261 case CTLENDVAR:
1262 putc('}', fp);
1263 break;
Ron Yorstona1b0d382020-07-23 08:32:27 +01001264#if BASH_PROCESS_SUBST
1265 case CTLTOPROC:
1266 putc('>', fp);
1267 goto backq;
1268 case CTLFROMPROC:
1269 putc('<', fp);
1270 goto backq;
1271#endif
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001272 case CTLBACKQ:
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001273 putc('$', fp);
Ron Yorstona1b0d382020-07-23 08:32:27 +01001274 IF_BASH_PROCESS_SUBST(backq:)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001275 putc('(', fp);
1276 shtree(bqlist->n, -1, NULL, fp);
1277 putc(')', fp);
1278 break;
1279 default:
1280 putc(*p, fp);
1281 break;
1282 }
1283 }
1284}
1285
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001286static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001287shcmd(union node *cmd, FILE *fp)
1288{
1289 union node *np;
1290 int first;
1291 const char *s;
1292 int dftfd;
1293
1294 first = 1;
1295 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001296 if (!first)
1297 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001298 sharg(np, fp);
1299 first = 0;
1300 }
1301 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001302 if (!first)
1303 putc(' ', fp);
1304 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001305 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001306 case NTO: s = ">>"+1; dftfd = 1; break;
1307 case NCLOBBER: s = ">|"; dftfd = 1; break;
1308 case NAPPEND: s = ">>"; dftfd = 1; break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01001309#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00001310 case NTO2:
1311#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001312 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001313 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001314 case NFROMFD: s = "<&"; break;
1315 case NFROMTO: s = "<>"; break;
1316 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001317 }
1318 if (np->nfile.fd != dftfd)
1319 fprintf(fp, "%d", np->nfile.fd);
1320 fputs(s, fp);
1321 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1322 fprintf(fp, "%d", np->ndup.dupfd);
1323 } else {
1324 sharg(np->nfile.fname, fp);
1325 }
1326 first = 0;
1327 }
1328}
1329
1330static void
1331shtree(union node *n, int ind, char *pfx, FILE *fp)
1332{
1333 struct nodelist *lp;
1334 const char *s;
1335
1336 if (n == NULL)
1337 return;
1338
1339 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001340
1341 if (n == NODE_EOF) {
1342 fputs("<EOF>", fp);
1343 return;
1344 }
1345
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001346 switch (n->type) {
1347 case NSEMI:
1348 s = "; ";
1349 goto binop;
1350 case NAND:
1351 s = " && ";
1352 goto binop;
1353 case NOR:
1354 s = " || ";
1355 binop:
1356 shtree(n->nbinary.ch1, ind, NULL, fp);
1357 /* if (ind < 0) */
1358 fputs(s, fp);
1359 shtree(n->nbinary.ch2, ind, NULL, fp);
1360 break;
1361 case NCMD:
1362 shcmd(n, fp);
1363 if (ind >= 0)
1364 putc('\n', fp);
1365 break;
1366 case NPIPE:
1367 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001368 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001369 if (lp->next)
1370 fputs(" | ", fp);
1371 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001372 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001373 fputs(" &", fp);
1374 if (ind >= 0)
1375 putc('\n', fp);
1376 break;
1377 default:
1378 fprintf(fp, "<node type %d>", n->type);
1379 if (ind >= 0)
1380 putc('\n', fp);
1381 break;
1382 }
1383}
1384
1385static void
1386showtree(union node *n)
1387{
1388 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001389 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001390}
1391
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001392#endif /* DEBUG */
1393
1394
Denis Vlasenkob012b102007-02-19 22:43:01 +00001395/* ============ Message printing */
1396
1397static void
1398ash_vmsg(const char *msg, va_list ap)
1399{
1400 fprintf(stderr, "%s: ", arg0);
1401 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001402 if (strcmp(arg0, commandname))
1403 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001404 if (!iflag || g_parsefile->pf_fd > 0)
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001405 fprintf(stderr, "line %d: ", errlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001406 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001407 vfprintf(stderr, msg, ap);
Denys Vlasenko9c541002015-10-07 15:44:36 +02001408 newline_and_flush(stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001409}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001410
1411/*
1412 * Exverror is called to raise the error exception. If the second argument
1413 * is not NULL then error prints an error message using printf style
1414 * formatting. It then raises the error exception.
1415 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001416static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001417static void
1418ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001419{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001420#if DEBUG
1421 if (msg) {
Denys Vlasenko474ed062016-10-30 18:30:29 +01001422 TRACE(("ash_vmsg_and_raise(%d):", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001423 TRACEV((msg, ap));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001424 } else
Denys Vlasenko474ed062016-10-30 18:30:29 +01001425 TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001426 if (msg)
1427#endif
1428 ash_vmsg(msg, ap);
1429
1430 flush_stdout_stderr();
1431 raise_exception(cond);
1432 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001433}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001434
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001435static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001436static void
1437ash_msg_and_raise_error(const char *msg, ...)
1438{
1439 va_list ap;
1440
Ron Yorstonea7d2f62017-01-03 11:18:23 +01001441 exitstatus = 2;
1442
Denis Vlasenkob012b102007-02-19 22:43:01 +00001443 va_start(ap, msg);
1444 ash_vmsg_and_raise(EXERROR, msg, ap);
1445 /* NOTREACHED */
1446 va_end(ap);
1447}
1448
Ron Yorstonbe366e52017-07-27 13:53:39 +01001449/*
Ron Yorstonbe366e52017-07-27 13:53:39 +01001450 * 'fmt' must be a string literal.
1451 */
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001452#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 +01001453
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001454static void raise_error_syntax(const char *) NORETURN;
1455static void
1456raise_error_syntax(const char *msg)
1457{
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001458 errlinno = g_parsefile->linno;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001459 ash_msg_and_raise_error("syntax error: %s", msg);
1460 /* NOTREACHED */
1461}
1462
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001463static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001464static void
1465ash_msg_and_raise(int cond, const char *msg, ...)
1466{
1467 va_list ap;
1468
1469 va_start(ap, msg);
1470 ash_vmsg_and_raise(cond, msg, ap);
1471 /* NOTREACHED */
1472 va_end(ap);
1473}
1474
1475/*
1476 * error/warning routines for external builtins
1477 */
1478static void
1479ash_msg(const char *fmt, ...)
1480{
1481 va_list ap;
1482
1483 va_start(ap, fmt);
1484 ash_vmsg(fmt, ap);
1485 va_end(ap);
1486}
1487
1488/*
1489 * Return a string describing an error. The returned string may be a
1490 * pointer to a static buffer that will be overwritten on the next call.
1491 * Action describes the operation that got the error.
1492 */
1493static const char *
1494errmsg(int e, const char *em)
1495{
1496 if (e == ENOENT || e == ENOTDIR) {
1497 return em;
1498 }
1499 return strerror(e);
1500}
1501
1502
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001503/* ============ Memory allocation */
1504
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001505#if 0
1506/* I consider these wrappers nearly useless:
1507 * ok, they return you to nearest exception handler, but
1508 * how much memory do you leak in the process, making
1509 * memory starvation worse?
1510 */
1511static void *
1512ckrealloc(void * p, size_t nbytes)
1513{
1514 p = realloc(p, nbytes);
1515 if (!p)
1516 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1517 return p;
1518}
1519
1520static void *
1521ckmalloc(size_t nbytes)
1522{
1523 return ckrealloc(NULL, nbytes);
1524}
1525
1526static void *
1527ckzalloc(size_t nbytes)
1528{
1529 return memset(ckmalloc(nbytes), 0, nbytes);
1530}
1531
1532static char *
1533ckstrdup(const char *s)
1534{
1535 char *p = strdup(s);
1536 if (!p)
1537 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1538 return p;
1539}
1540#else
1541/* Using bbox equivalents. They exit if out of memory */
1542# define ckrealloc xrealloc
1543# define ckmalloc xmalloc
1544# define ckzalloc xzalloc
1545# define ckstrdup xstrdup
1546#endif
1547
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001548/*
1549 * It appears that grabstackstr() will barf with such alignments
1550 * because stalloc() will return a string allocated in a new stackblock.
1551 */
1552#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1553enum {
1554 /* Most machines require the value returned from malloc to be aligned
1555 * in some way. The following macro will get this right
1556 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001557 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001558 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001559 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001560};
1561
1562struct stack_block {
1563 struct stack_block *prev;
1564 char space[MINSIZE];
1565};
1566
1567struct stackmark {
1568 struct stack_block *stackp;
1569 char *stacknxt;
1570 size_t stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001571};
1572
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001573
Denis Vlasenko01631112007-12-16 17:20:38 +00001574struct globals_memstack {
1575 struct stack_block *g_stackp; // = &stackbase;
Denis Vlasenko01631112007-12-16 17:20:38 +00001576 char *g_stacknxt; // = stackbase.space;
1577 char *sstrend; // = stackbase.space + MINSIZE;
1578 size_t g_stacknleft; // = MINSIZE;
Denis Vlasenko01631112007-12-16 17:20:38 +00001579 struct stack_block stackbase;
1580};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01001581extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001582#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001583#define g_stackp (G_memstack.g_stackp )
Denis Vlasenko01631112007-12-16 17:20:38 +00001584#define g_stacknxt (G_memstack.g_stacknxt )
1585#define sstrend (G_memstack.sstrend )
1586#define g_stacknleft (G_memstack.g_stacknleft)
Denis Vlasenko01631112007-12-16 17:20:38 +00001587#define stackbase (G_memstack.stackbase )
1588#define INIT_G_memstack() do { \
YU Jincheng5156b242021-10-10 02:19:51 +08001589 XZALLOC_CONST_PTR(&ash_ptr_to_globals_memstack, sizeof(G_memstack)); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001590 g_stackp = &stackbase; \
1591 g_stacknxt = stackbase.space; \
1592 g_stacknleft = MINSIZE; \
1593 sstrend = stackbase.space + MINSIZE; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001594} while (0)
1595
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001596
Denis Vlasenko01631112007-12-16 17:20:38 +00001597#define stackblock() ((void *)g_stacknxt)
1598#define stackblocksize() g_stacknleft
1599
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001600/*
1601 * Parse trees for commands are allocated in lifo order, so we use a stack
1602 * to make this more efficient, and also to avoid all sorts of exception
1603 * handling code to handle interrupts in the middle of a parse.
1604 *
1605 * The size 504 was chosen because the Ultrix malloc handles that size
1606 * well.
1607 */
1608static void *
1609stalloc(size_t nbytes)
1610{
1611 char *p;
1612 size_t aligned;
1613
1614 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001615 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001616 size_t len;
1617 size_t blocksize;
1618 struct stack_block *sp;
1619
1620 blocksize = aligned;
1621 if (blocksize < MINSIZE)
1622 blocksize = MINSIZE;
1623 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1624 if (len < blocksize)
1625 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1626 INT_OFF;
1627 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001628 sp->prev = g_stackp;
1629 g_stacknxt = sp->space;
1630 g_stacknleft = blocksize;
1631 sstrend = g_stacknxt + blocksize;
1632 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001633 INT_ON;
1634 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001635 p = g_stacknxt;
1636 g_stacknxt += aligned;
1637 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001638 return p;
1639}
1640
Denis Vlasenko597906c2008-02-20 16:38:54 +00001641static void *
1642stzalloc(size_t nbytes)
1643{
1644 return memset(stalloc(nbytes), 0, nbytes);
1645}
1646
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001647static void
1648stunalloc(void *p)
1649{
1650#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001651 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001652 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001653 abort();
1654 }
1655#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001656 g_stacknleft += g_stacknxt - (char *)p;
1657 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001658}
1659
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001660/*
1661 * Like strdup but works with the ash stack.
1662 */
1663static char *
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02001664sstrdup(const char *p)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001665{
1666 size_t len = strlen(p) + 1;
1667 return memcpy(stalloc(len), p, len);
1668}
1669
Denys Vlasenko03c36e02018-01-10 15:18:35 +01001670static ALWAYS_INLINE void
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001671grabstackblock(size_t len)
1672{
Denys Vlasenkoa318bba2016-10-26 18:26:27 +02001673 stalloc(len);
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001674}
1675
1676static void
1677pushstackmark(struct stackmark *mark, size_t len)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001678{
Denis Vlasenko01631112007-12-16 17:20:38 +00001679 mark->stackp = g_stackp;
1680 mark->stacknxt = g_stacknxt;
1681 mark->stacknleft = g_stacknleft;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001682 grabstackblock(len);
1683}
1684
1685static void
1686setstackmark(struct stackmark *mark)
1687{
1688 pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001689}
1690
1691static void
1692popstackmark(struct stackmark *mark)
1693{
1694 struct stack_block *sp;
1695
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001696 if (!mark->stackp)
1697 return;
1698
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001699 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001700 while (g_stackp != mark->stackp) {
1701 sp = g_stackp;
1702 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001703 free(sp);
1704 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001705 g_stacknxt = mark->stacknxt;
1706 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001707 sstrend = mark->stacknxt + mark->stacknleft;
1708 INT_ON;
1709}
1710
1711/*
1712 * When the parser reads in a string, it wants to stick the string on the
1713 * stack and only adjust the stack pointer when it knows how big the
1714 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1715 * of space on top of the stack and stackblocklen returns the length of
1716 * this block. Growstackblock will grow this space by at least one byte,
1717 * possibly moving it (like realloc). Grabstackblock actually allocates the
1718 * part of the block that has been used.
1719 */
1720static void
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001721growstackblock(size_t min)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001722{
1723 size_t newlen;
1724
Denis Vlasenko01631112007-12-16 17:20:38 +00001725 newlen = g_stacknleft * 2;
1726 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001727 ash_msg_and_raise_error(bb_msg_memory_exhausted);
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001728 min = SHELL_ALIGN(min | 128);
1729 if (newlen < min)
1730 newlen += min;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001731
Denis Vlasenko01631112007-12-16 17:20:38 +00001732 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001733 struct stack_block *sp;
1734 struct stack_block *prevstackp;
1735 size_t grosslen;
1736
1737 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001738 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001739 prevstackp = sp->prev;
1740 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1741 sp = ckrealloc(sp, grosslen);
1742 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001743 g_stackp = sp;
1744 g_stacknxt = sp->space;
1745 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001746 sstrend = sp->space + newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001747 INT_ON;
1748 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001750 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001751 char *p = stalloc(newlen);
1752
1753 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001754 g_stacknxt = memcpy(p, oldspace, oldlen);
1755 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001756 }
1757}
1758
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001759/*
1760 * The following routines are somewhat easier to use than the above.
1761 * The user declares a variable of type STACKSTR, which may be declared
1762 * to be a register. The macro STARTSTACKSTR initializes things. Then
1763 * the user uses the macro STPUTC to add characters to the string. In
1764 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1765 * grown as necessary. When the user is done, she can just leave the
1766 * string there and refer to it using stackblock(). Or she can allocate
1767 * the space for it using grabstackstr(). If it is necessary to allow
1768 * someone else to use the stack temporarily and then continue to grow
1769 * the string, the user should use grabstack to allocate the space, and
1770 * then call ungrabstr(p) to return to the previous mode of operation.
1771 *
1772 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1773 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1774 * is space for at least one character.
1775 */
1776static void *
1777growstackstr(void)
1778{
1779 size_t len = stackblocksize();
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001780 growstackblock(0);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001781 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001782}
1783
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001784static char *
1785growstackto(size_t len)
1786{
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001787 if (stackblocksize() < len)
1788 growstackblock(len);
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001789 return stackblock();
1790}
1791
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001792/*
1793 * Called from CHECKSTRSPACE.
1794 */
1795static char *
1796makestrspace(size_t newlen, char *p)
1797{
Denis Vlasenko01631112007-12-16 17:20:38 +00001798 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001799
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001800 return growstackto(len + newlen) + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001801}
1802
1803static char *
Denys Vlasenko538ee412020-02-22 19:11:41 +01001804stnputs(const char *s, size_t n, char *p)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001805{
1806 p = makestrspace(n, p);
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001807 p = (char *)mempcpy(p, s, n);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001808 return p;
1809}
1810
1811static char *
1812stack_putstr(const char *s, char *p)
1813{
Denys Vlasenko538ee412020-02-22 19:11:41 +01001814 return stnputs(s, strlen(s), p);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001815}
1816
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001817static char *
1818_STPUTC(int c, char *p)
1819{
1820 if (p == sstrend)
1821 p = growstackstr();
1822 *p++ = c;
1823 return p;
1824}
1825
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001826#define STARTSTACKSTR(p) ((p) = stackblock())
1827#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001828#define CHECKSTRSPACE(n, p) do { \
1829 char *q = (p); \
1830 size_t l = (n); \
1831 size_t m = sstrend - q; \
1832 if (l > m) \
1833 (p) = makestrspace(l, q); \
1834} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001835#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001836#define STACKSTRNUL(p) do { \
1837 if ((p) == sstrend) \
1838 (p) = growstackstr(); \
1839 *(p) = '\0'; \
1840} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001841#define STUNPUTC(p) (--(p))
1842#define STTOPC(p) ((p)[-1])
1843#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844
1845#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001846#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001847#define stackstrend() ((void *)sstrend)
1848
1849
1850/* ============ String helpers */
1851
1852/*
1853 * prefix -- see if pfx is a prefix of string.
1854 */
1855static char *
1856prefix(const char *string, const char *pfx)
1857{
1858 while (*pfx) {
1859 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001860 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001861 }
1862 return (char *) string;
1863}
1864
1865/*
1866 * Check for a valid number. This should be elsewhere.
1867 */
1868static int
1869is_number(const char *p)
1870{
1871 do {
1872 if (!isdigit(*p))
1873 return 0;
1874 } while (*++p != '\0');
1875 return 1;
1876}
1877
1878/*
1879 * Convert a string of digits to an integer, printing an error message on
1880 * failure.
1881 */
1882static int
1883number(const char *s)
1884{
1885 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001886 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001887 return atoi(s);
1888}
1889
1890/*
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001891 * Produce a single quoted string suitable as input to the shell.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001892 * The return string is allocated on the stack.
1893 */
1894static char *
1895single_quote(const char *s)
1896{
1897 char *p;
1898
1899 STARTSTACKSTR(p);
1900
1901 do {
1902 char *q;
1903 size_t len;
1904
1905 len = strchrnul(s, '\'') - s;
1906
1907 q = p = makestrspace(len + 3, p);
1908
1909 *q++ = '\'';
Denys Vlasenko94af83e2017-07-23 21:55:40 +02001910 q = (char *)mempcpy(q, s, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911 *q++ = '\'';
1912 s += len;
1913
1914 STADJUST(q - p, p);
1915
Denys Vlasenkocd716832009-11-28 22:14:02 +01001916 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001917 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001918 len = 0;
1919 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001920
1921 q = p = makestrspace(len + 3, p);
1922
1923 *q++ = '"';
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001924 q = (char *)mempcpy(q, s - len, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001925 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001926
1927 STADJUST(q - p, p);
1928 } while (*s);
1929
Denys Vlasenkocd716832009-11-28 22:14:02 +01001930 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001931
1932 return stackblock();
1933}
1934
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001935/*
1936 * Produce a possibly single quoted string suitable as input to the shell.
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001937 * If quoting was done, the return string is allocated on the stack,
1938 * otherwise a pointer to the original string is returned.
1939 */
1940static const char *
1941maybe_single_quote(const char *s)
1942{
1943 const char *p = s;
1944
1945 while (*p) {
1946 /* Assuming ACSII */
1947 /* quote ctrl_chars space !"#$%&'()* */
1948 if (*p < '+')
1949 goto need_quoting;
1950 /* quote ;<=>? */
1951 if (*p >= ';' && *p <= '?')
1952 goto need_quoting;
1953 /* quote `[\ */
1954 if (*p == '`')
1955 goto need_quoting;
1956 if (*p == '[')
1957 goto need_quoting;
1958 if (*p == '\\')
1959 goto need_quoting;
1960 /* quote {|}~ DEL and high bytes */
1961 if (*p > 'z')
1962 goto need_quoting;
1963 /* Not quoting these: +,-./ 0-9 :@ A-Z ]^_ a-z */
1964 /* TODO: maybe avoid quoting % */
1965 p++;
1966 }
1967 return s;
1968
1969 need_quoting:
1970 return single_quote(s);
1971}
1972
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001973
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001974/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001975
1976static char **argptr; /* argument list for builtin commands */
1977static char *optionarg; /* set by nextopt (like getopt) */
1978static char *optptr; /* used by nextopt */
1979
1980/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001981 * XXX - should get rid of. Have all builtins use getopt(3).
1982 * The library getopt must have the BSD extension static variable
1983 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001985 * Standard option processing (a la getopt) for builtin routines.
1986 * The only argument that is passed to nextopt is the option string;
1987 * the other arguments are unnecessary. It returns the character,
1988 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001989 */
1990static int
1991nextopt(const char *optstring)
1992{
1993 char *p;
1994 const char *q;
1995 char c;
1996
1997 p = optptr;
1998 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001999 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002000 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002001 if (p == NULL)
2002 return '\0';
2003 if (*p != '-')
2004 return '\0';
2005 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002006 return '\0';
2007 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002008 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002009 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002010 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002011 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002012 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002013 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00002014 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002015 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00002016 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002017 if (*++q == ':')
2018 q++;
2019 }
2020 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002021 if (*p == '\0') {
2022 p = *argptr++;
2023 if (p == NULL)
2024 ash_msg_and_raise_error("no arg for -%c option", c);
2025 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002026 optionarg = p;
2027 p = NULL;
2028 }
2029 optptr = p;
2030 return c;
2031}
2032
2033
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002034/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002035
Denis Vlasenko01631112007-12-16 17:20:38 +00002036struct shparam {
2037 int nparam; /* # of positional parameters (without $0) */
2038#if ENABLE_ASH_GETOPTS
2039 int optind; /* next parameter to be processed by getopts */
2040 int optoff; /* used by getopts */
2041#endif
2042 unsigned char malloced; /* if parameter list dynamically allocated */
2043 char **p; /* parameter list */
2044};
2045
2046/*
2047 * Free the list of positional parameters.
2048 */
2049static void
2050freeparam(volatile struct shparam *param)
2051{
Denis Vlasenko01631112007-12-16 17:20:38 +00002052 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00002053 char **ap, **ap1;
2054 ap = ap1 = param->p;
2055 while (*ap)
2056 free(*ap++);
2057 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00002058 }
2059}
2060
2061#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002062static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00002063#endif
2064
2065struct var {
2066 struct var *next; /* next entry in hash list */
2067 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002068 const char *var_text; /* name=value */
2069 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00002070 /* the variable gets set/unset */
2071};
2072
2073struct localvar {
2074 struct localvar *next; /* next local variable in list */
2075 struct var *vp; /* the variable that was made local */
2076 int flags; /* saved flags */
2077 const char *text; /* saved text */
2078};
2079
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002080/* flags */
2081#define VEXPORT 0x01 /* variable is exported */
2082#define VREADONLY 0x02 /* variable cannot be modified */
2083#define VSTRFIXED 0x04 /* variable struct is statically allocated */
2084#define VTEXTFIXED 0x08 /* text is statically allocated */
2085#define VSTACK 0x10 /* text is allocated on the stack */
2086#define VUNSET 0x20 /* the variable is not set */
2087#define VNOFUNC 0x40 /* don't call the callback function */
2088#define VNOSET 0x80 /* do not set variable - just readonly test */
2089#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002090#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002091# define VDYNAMIC 0x200 /* dynamic variable */
2092#else
2093# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094#endif
2095
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002096
Denis Vlasenko01631112007-12-16 17:20:38 +00002097/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002098#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002099static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002100change_lc_all(const char *value)
2101{
2102 if (value && *value != '\0')
2103 setlocale(LC_ALL, value);
2104}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002105static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002106change_lc_ctype(const char *value)
2107{
2108 if (value && *value != '\0')
2109 setlocale(LC_CTYPE, value);
2110}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002111#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112#if ENABLE_ASH_MAIL
2113static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01002114static void changemail(const char *var_value) FAST_FUNC;
2115#else
2116# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002117#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002118static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002120static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002121#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002122#if BASH_EPOCH_VARS
2123static void change_seconds(const char *) FAST_FUNC;
2124static void change_realtime(const char *) FAST_FUNC;
2125#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002126
Denis Vlasenko01631112007-12-16 17:20:38 +00002127static const struct {
2128 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002129 const char *var_text;
2130 void (*var_func)(const char *) FAST_FUNC;
Denys Vlasenko965b7952020-11-30 13:03:03 +01002131} varinit_data[] ALIGN_PTR = {
Denys Vlasenko566a3132012-07-07 21:40:35 +02002132 /*
2133 * Note: VEXPORT would not work correctly here for NOFORK applets:
2134 * some environment strings may be constant.
2135 */
Denis Vlasenko01631112007-12-16 17:20:38 +00002136 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002137#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002138 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
2139 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002140#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00002141 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
2142 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
2143 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
2144 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002145#if ENABLE_ASH_GETOPTS
Denys Vlasenkoe627ac92016-09-30 14:36:59 +02002146 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002147#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002148 { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL },
Denys Vlasenko704c5962021-09-15 19:31:44 +02002149 { VSTRFIXED|VTEXTFIXED , NULL /* inited to funcnamevar */, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002150#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002151 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002152#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002153#if BASH_EPOCH_VARS
2154 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds },
2155 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime },
2156#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002157#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002158 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
2159 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002160#endif
2161#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002162 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002163#endif
2164};
2165
Denis Vlasenko0b769642008-07-24 07:54:57 +00002166struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00002167
2168struct globals_var {
2169 struct shparam shellparam; /* $@ current positional parameters */
2170 struct redirtab *redirlist;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02002171 int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */
Denis Vlasenko01631112007-12-16 17:20:38 +00002172 struct var *vartab[VTABSIZE];
2173 struct var varinit[ARRAY_SIZE(varinit_data)];
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002174 int lineno;
2175 char linenovar[sizeof("LINENO=") + sizeof(int)*3];
Denys Vlasenko704c5962021-09-15 19:31:44 +02002176 char funcnamevar[sizeof("FUNCNAME=") + 64];
2177 char *funcname;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02002178 unsigned trap_depth;
2179 bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */
Denis Vlasenko01631112007-12-16 17:20:38 +00002180};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01002181extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00002182#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00002183#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00002184//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00002185#define preverrout_fd (G_var.preverrout_fd)
2186#define vartab (G_var.vartab )
2187#define varinit (G_var.varinit )
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002188#define lineno (G_var.lineno )
2189#define linenovar (G_var.linenovar )
Denys Vlasenko704c5962021-09-15 19:31:44 +02002190#define funcnamevar (G_var.funcnamevar )
2191#define funcname (G_var.funcname )
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02002192#define trap_depth (G_var.trap_depth )
2193#define in_trap_ERR (G_var.in_trap_ERR )
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002194#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002195#if ENABLE_ASH_MAIL
Ron Yorston1d371862019-04-15 10:52:05 +01002196# define vmail varinit[1]
2197# define vmpath varinit[2]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002198#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002199#define VAR_OFFSET1 (ENABLE_ASH_MAIL*2)
2200#define vpath varinit[VAR_OFFSET1 + 1]
2201#define vps1 varinit[VAR_OFFSET1 + 2]
2202#define vps2 varinit[VAR_OFFSET1 + 3]
2203#define vps4 varinit[VAR_OFFSET1 + 4]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002204#if ENABLE_ASH_GETOPTS
Ron Yorston1d371862019-04-15 10:52:05 +01002205# define voptind varinit[VAR_OFFSET1 + 5]
2206#endif
2207#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS)
2208#define vlineno varinit[VAR_OFFSET2 + 5]
Denys Vlasenko704c5962021-09-15 19:31:44 +02002209#define vfuncname varinit[VAR_OFFSET2 + 6]
Ron Yorston1d371862019-04-15 10:52:05 +01002210#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko704c5962021-09-15 19:31:44 +02002211# define vrandom varinit[VAR_OFFSET2 + 7]
Ron Yorston1d371862019-04-15 10:52:05 +01002212#endif
2213#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT)
2214#if BASH_EPOCH_VARS
Denys Vlasenko704c5962021-09-15 19:31:44 +02002215# define vepochs varinit[VAR_OFFSET3 + 7]
2216# define vepochr varinit[VAR_OFFSET3 + 8]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002217#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002218#define INIT_G_var() do { \
2219 unsigned i; \
YU Jincheng5156b242021-10-10 02:19:51 +08002220 XZALLOC_CONST_PTR(&ash_ptr_to_globals_var, sizeof(G_var)); \
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002221 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
2222 varinit[i].flags = varinit_data[i].flags; \
2223 varinit[i].var_text = varinit_data[i].var_text; \
2224 varinit[i].var_func = varinit_data[i].var_func; \
2225 } \
2226 strcpy(linenovar, "LINENO="); \
2227 vlineno.var_text = linenovar; \
Denys Vlasenko704c5962021-09-15 19:31:44 +02002228 strcpy(funcnamevar, "FUNCNAME="); \
2229 vfuncname.var_text = funcnamevar; \
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002230} while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231
2232/*
2233 * The following macros access the values of the above variables.
2234 * They have to skip over the name. They return the null string
2235 * for unset variables.
2236 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002237#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002238#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002239#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002240# define mailval() (vmail.var_text + 5)
2241# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002242# define mpathset() ((vmpath.flags & VUNSET) == 0)
2243#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002244#define pathval() (vpath.var_text + 5)
2245#define ps1val() (vps1.var_text + 4)
2246#define ps2val() (vps2.var_text + 4)
2247#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002248#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002249# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002250#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002251
Denis Vlasenko01631112007-12-16 17:20:38 +00002252#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002253static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002254getoptsreset(const char *value)
2255{
Denys Vlasenko46289452017-08-11 00:59:36 +02002256 shellparam.optind = 1;
2257 if (is_number(value))
2258 shellparam.optind = number(value) ?: 1;
Denis Vlasenko01631112007-12-16 17:20:38 +00002259 shellparam.optoff = -1;
2260}
2261#endif
2262
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002263/*
2264 * Compares two strings up to the first = or '\0'. The first
2265 * string must be terminated by '='; the second may be terminated by
2266 * either '=' or '\0'.
2267 */
2268static int
2269varcmp(const char *p, const char *q)
2270{
2271 int c, d;
2272
2273 while ((c = *p) == (d = *q)) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002274 if (c == '\0' || c == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002275 goto out;
2276 p++;
2277 q++;
2278 }
2279 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002280 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002281 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002282 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002283 out:
2284 return c - d;
2285}
2286
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002287/*
2288 * Find the appropriate entry in the hash table from the name.
2289 */
2290static struct var **
2291hashvar(const char *p)
2292{
2293 unsigned hashval;
2294
2295 hashval = ((unsigned char) *p) << 4;
2296 while (*p && *p != '=')
2297 hashval += (unsigned char) *p++;
2298 return &vartab[hashval % VTABSIZE];
2299}
2300
2301static int
2302vpcmp(const void *a, const void *b)
2303{
2304 return varcmp(*(const char **)a, *(const char **)b);
2305}
2306
2307/*
2308 * This routine initializes the builtin variables.
2309 */
2310static void
2311initvar(void)
2312{
2313 struct var *vp;
2314 struct var *end;
2315 struct var **vpp;
2316
2317 /*
2318 * PS1 depends on uid
2319 */
2320#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002321 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002322#else
2323 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002324 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002325#endif
2326 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002327 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002328 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002329 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002330 vp->next = *vpp;
2331 *vpp = vp;
2332 } while (++vp < end);
2333}
2334
2335static struct var **
2336findvar(struct var **vpp, const char *name)
2337{
2338 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002339 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002340 break;
2341 }
2342 }
2343 return vpp;
2344}
2345
2346/*
2347 * Find the value of a variable. Returns NULL if not set.
2348 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002349static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002350lookupvar(const char *name)
2351{
2352 struct var *v;
2353
2354 v = *findvar(hashvar(name), name);
2355 if (v) {
Ron Yorston1d371862019-04-15 10:52:05 +01002356#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002357 /*
2358 * Dynamic variables are implemented roughly the same way they are
2359 * in bash. Namely, they're "special" so long as they aren't unset.
2360 * As soon as they're unset, they're no longer dynamic, and dynamic
2361 * lookup will no longer happen at that point. -- PFM.
2362 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002363 if (v->flags & VDYNAMIC)
2364 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002365#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002366 if (!(v->flags & VUNSET)) {
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02002367 if (v->var_text == linenovar) {
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002368 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
Denys Vlasenko704c5962021-09-15 19:31:44 +02002369 } else
2370 if (v->var_text == funcnamevar) {
2371 safe_strncpy(funcnamevar+9, funcname ? funcname : "", sizeof(funcnamevar)-9);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002372 }
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002373 return var_end(v->var_text);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002374 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002375 }
2376 return NULL;
2377}
2378
Denys Vlasenko0b883582016-12-23 16:49:07 +01002379#if ENABLE_UNICODE_SUPPORT
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02002380static void
2381reinit_unicode_for_ash(void)
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002382{
2383 /* Unicode support should be activated even if LANG is set
2384 * _during_ shell execution, not only if it was set when
2385 * shell was started. Therefore, re-check LANG every time:
2386 */
2387 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2388 || ENABLE_UNICODE_USING_LOCALE
2389 ) {
2390 const char *s = lookupvar("LC_ALL");
2391 if (!s) s = lookupvar("LC_CTYPE");
2392 if (!s) s = lookupvar("LANG");
2393 reinit_unicode(s);
2394 }
2395}
Denys Vlasenko0b883582016-12-23 16:49:07 +01002396#else
2397# define reinit_unicode_for_ash() ((void)0)
2398#endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002399
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002400/*
2401 * Search the environment of a builtin command.
2402 */
Denys Vlasenko488e6092017-07-26 23:08:36 +02002403static ALWAYS_INLINE const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002404bltinlookup(const char *name)
2405{
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002406 return lookupvar(name);
2407}
2408
2409/*
2410 * Same as setvar except that the variable and value are passed in
2411 * the first argument as name=value. Since the first argument will
2412 * be actually stored in the table, it should not be a string that
2413 * will go away.
2414 * Called with interrupts off.
2415 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002416static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002417setvareq(char *s, int flags)
2418{
2419 struct var *vp, **vpp;
2420
2421 vpp = hashvar(s);
2422 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002423 vpp = findvar(vpp, s);
2424 vp = *vpp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002425 if (vp) {
2426 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2427 const char *n;
2428
2429 if (flags & VNOSAVE)
2430 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002431 n = vp->var_text;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +02002432 exitstatus = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002433 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2434 }
2435
2436 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002437 goto out;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002438
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002439 if (vp->var_func && !(flags & VNOFUNC))
2440 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002441
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002442 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2443 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002444
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002445 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2446 *vpp = vp->next;
2447 free(vp);
2448 out_free:
2449 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2450 free(s);
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002451 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002452 }
2453
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002454 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
Ron Yorston1d371862019-04-15 10:52:05 +01002455#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Ron Yorstond96c69d2019-04-15 10:49:35 +01002456 if (flags & VUNSET)
2457 flags &= ~VDYNAMIC;
2458#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002459 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002460 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002461 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002462 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002463 if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
2464 goto out_free;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002465 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002466 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002467 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002468 *vpp = vp;
2469 }
2470 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2471 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002472 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002473 vp->flags = flags;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002474
2475 out:
2476 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002477}
2478
2479/*
2480 * Set the value of a variable. The flags argument is ored with the
2481 * flags of the variable. If val is NULL, the variable is unset.
2482 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002483static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002484setvar(const char *name, const char *val, int flags)
2485{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002486 const char *q;
2487 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002488 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002489 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002490 size_t vallen;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002491 struct var *vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002492
2493 q = endofname(name);
2494 p = strchrnul(q, '=');
2495 namelen = p - name;
2496 if (!namelen || p != q)
2497 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2498 vallen = 0;
2499 if (val == NULL) {
2500 flags |= VUNSET;
2501 } else {
2502 vallen = strlen(val);
2503 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002504
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002505 INT_OFF;
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002506 nameeq = ckzalloc(namelen + vallen + 2);
Denys Vlasenkoda2244f2017-07-21 18:51:29 +02002507 p = mempcpy(nameeq, name, namelen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002508 if (val) {
2509 *p++ = '=';
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002510 memcpy(p, val, vallen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002511 }
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002512 vp = setvareq(nameeq, flags | VNOSAVE);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002513 INT_ON;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002514
2515 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002516}
2517
Denys Vlasenko03dad222010-01-12 23:29:57 +01002518static void FAST_FUNC
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002519setvar0(const char *name, const char *val)
Denys Vlasenko03dad222010-01-12 23:29:57 +01002520{
2521 setvar(name, val, 0);
2522}
2523
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002524/*
2525 * Unset the specified variable.
2526 */
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002527static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002528unsetvar(const char *s)
2529{
Denys Vlasenkocf3a7962017-07-26 14:38:19 +02002530 setvar(s, NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002531}
2532
2533/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002534 * Generate a list of variables satisfying the given conditions.
2535 */
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002536#if !ENABLE_FEATURE_SH_NOFORK
2537# define listvars(on, off, lp, end) listvars(on, off, end)
2538#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002539static char **
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002540listvars(int on, int off, struct strlist *lp, char ***end)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002541{
2542 struct var **vpp;
2543 struct var *vp;
2544 char **ep;
2545 int mask;
2546
2547 STARTSTACKSTR(ep);
2548 vpp = vartab;
2549 mask = on | off;
2550 do {
2551 for (vp = *vpp; vp; vp = vp->next) {
2552 if ((vp->flags & mask) == on) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002553#if ENABLE_FEATURE_SH_NOFORK
2554 /* If variable with the same name is both
2555 * exported and temporarily set for a command:
2556 * export ZVAR=5
2557 * ZVAR=6 printenv
2558 * then "ZVAR=6" will be both in vartab and
2559 * lp lists. Do not pass it twice to printenv.
2560 */
2561 struct strlist *lp1 = lp;
2562 while (lp1) {
2563 if (strcmp(lp1->text, vp->var_text) == 0)
2564 goto skip;
2565 lp1 = lp1->next;
2566 }
2567#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002568 if (ep == stackstrend())
2569 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002570 *ep++ = (char*)vp->var_text;
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002571#if ENABLE_FEATURE_SH_NOFORK
2572 skip: ;
2573#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002574 }
2575 }
2576 } while (++vpp < vartab + VTABSIZE);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002577
2578#if ENABLE_FEATURE_SH_NOFORK
2579 while (lp) {
2580 if (ep == stackstrend())
2581 ep = growstackstr();
2582 *ep++ = lp->text;
2583 lp = lp->next;
2584 }
2585#endif
2586
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002587 if (ep == stackstrend())
2588 ep = growstackstr();
2589 if (end)
2590 *end = ep;
2591 *ep++ = NULL;
2592 return grabstackstr(ep);
2593}
2594
2595
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002596/* ============ Path search helper */
2597static const char *
2598legal_pathopt(const char *opt, const char *term, int magic)
2599{
2600 switch (magic) {
2601 case 0:
2602 opt = NULL;
2603 break;
2604
2605 case 1:
2606 opt = prefix(opt, "builtin") ?: prefix(opt, "func");
2607 break;
2608
2609 default:
2610 opt += strcspn(opt, term);
2611 break;
2612 }
2613
2614 if (opt && *opt == '%')
2615 opt++;
2616
2617 return opt;
2618}
2619
2620/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002621 * The variable path (passed by reference) should be set to the start
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002622 * of the path before the first call; padvance will update
2623 * this value as it proceeds. Successive calls to padvance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002624 * the possible path expansions in sequence. If an option (indicated by
2625 * a percent sign) appears in the path entry then the global variable
2626 * pathopt will be set to point to it; otherwise pathopt will be set to
2627 * NULL.
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002628 *
2629 * If magic is 0 then pathopt recognition will be disabled. If magic is
2630 * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
2631 * pathopt.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002632 */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002633static const char *pathopt; /* set by padvance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002634
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002635static int
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002636padvance_magic(const char **path, const char *name, int magic)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002637{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002638 const char *term = "%:";
2639 const char *lpathopt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002640 const char *p;
2641 char *q;
2642 const char *start;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002643 size_t qlen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002644 size_t len;
2645
2646 if (*path == NULL)
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002647 return -1;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002648
2649 lpathopt = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002650 start = *path;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002651
2652 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2653 lpathopt = start + 1;
2654 start = p;
2655 term = ":";
2656 }
2657
2658 len = strcspn(start, term);
2659 p = start + len;
2660
2661 if (*p == '%') {
2662 size_t extra = strchrnul(p, ':') - p;
2663
2664 if (legal_pathopt(p + 1, term, magic))
2665 lpathopt = p + 1;
2666 else
2667 len += extra;
2668
2669 p += extra;
2670 }
2671
2672 pathopt = lpathopt;
2673 *path = *p == ':' ? p + 1 : NULL;
2674
2675 /* "2" is for '/' and '\0' */
2676 qlen = len + strlen(name) + 2;
2677 q = growstackto(qlen);
2678
2679 if (len) {
2680 q = mempcpy(q, start, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002681 *q++ = '/';
2682 }
2683 strcpy(q, name);
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002684
2685 return qlen;
2686}
2687
2688static int
2689padvance(const char **path, const char *name)
2690{
2691 return padvance_magic(path, name, 1);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002692}
2693
2694
2695/* ============ Prompt */
2696
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002697static smallint doprompt; /* if set, prompt the user */
2698static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002699
2700#if ENABLE_FEATURE_EDITING
2701static line_input_t *line_input_state;
2702static const char *cmdedit_prompt;
2703static void
2704putprompt(const char *s)
2705{
2706 if (ENABLE_ASH_EXPAND_PRMT) {
2707 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002708 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002709 return;
2710 }
2711 cmdedit_prompt = s;
2712}
2713#else
2714static void
2715putprompt(const char *s)
2716{
2717 out2str(s);
2718}
2719#endif
2720
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002721/* expandstr() needs parsing machinery, so it is far away ahead... */
Denys Vlasenko46999802017-07-29 21:12:29 +02002722static const char *expandstr(const char *ps, int syntax_type);
2723/* Values for syntax param */
2724#define BASESYNTAX 0 /* not in quotes */
2725#define DQSYNTAX 1 /* in double quotes */
2726#define SQSYNTAX 2 /* in single quotes */
2727#define ARISYNTAX 3 /* in arithmetic */
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +02002728#if ENABLE_ASH_EXPAND_PRMT
2729# define PSSYNTAX 4 /* prompt. never passed to SIT() */
2730#endif
Denys Vlasenko46999802017-07-29 21:12:29 +02002731/* PSSYNTAX expansion is identical to DQSYNTAX, except keeping '\$' as '\$' */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002732
Denys Vlasenko46999802017-07-29 21:12:29 +02002733/*
2734 * called by editline -- any expansions to the prompt should be added here.
2735 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002736static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002737setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002738{
2739 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002740 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2741
2742 if (!do_set)
2743 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002744
2745 needprompt = 0;
2746
2747 switch (whichprompt) {
2748 case 1:
2749 prompt = ps1val();
2750 break;
2751 case 2:
2752 prompt = ps2val();
2753 break;
2754 default: /* 0 */
2755 prompt = nullstr;
2756 }
2757#if ENABLE_ASH_EXPAND_PRMT
Denys Vlasenko60ca8342016-09-30 11:21:21 +02002758 pushstackmark(&smark, stackblocksize());
Denys Vlasenko46999802017-07-29 21:12:29 +02002759 putprompt(expandstr(prompt, PSSYNTAX));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002760 popstackmark(&smark);
Denys Vlasenko48c803a2017-07-01 23:24:48 +02002761#else
2762 putprompt(prompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002763#endif
2764}
2765
2766
2767/* ============ The cd and pwd commands */
2768
2769#define CD_PHYSICAL 1
2770#define CD_PRINT 2
2771
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002772static int
2773cdopt(void)
2774{
2775 int flags = 0;
2776 int i, j;
2777
2778 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002779 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002780 if (i != j) {
2781 flags ^= CD_PHYSICAL;
2782 j = i;
2783 }
2784 }
2785
2786 return flags;
2787}
2788
2789/*
2790 * Update curdir (the name of the current directory) in response to a
2791 * cd command.
2792 */
2793static const char *
2794updatepwd(const char *dir)
2795{
2796 char *new;
2797 char *p;
2798 char *cdcomppath;
2799 const char *lim;
2800
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02002801 cdcomppath = sstrdup(dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002802 STARTSTACKSTR(new);
2803 if (*dir != '/') {
2804 if (curdir == nullstr)
2805 return 0;
2806 new = stack_putstr(curdir, new);
2807 }
2808 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002809 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002810 if (*dir != '/') {
2811 if (new[-1] != '/')
2812 USTPUTC('/', new);
2813 if (new > lim && *lim == '/')
2814 lim++;
2815 } else {
2816 USTPUTC('/', new);
2817 cdcomppath++;
2818 if (dir[1] == '/' && dir[2] != '/') {
2819 USTPUTC('/', new);
2820 cdcomppath++;
2821 lim++;
2822 }
2823 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002824 p = strtok_r(cdcomppath, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002825 while (p) {
2826 switch (*p) {
2827 case '.':
2828 if (p[1] == '.' && p[2] == '\0') {
2829 while (new > lim) {
2830 STUNPUTC(new);
2831 if (new[-1] == '/')
2832 break;
2833 }
2834 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002835 }
2836 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002837 break;
2838 /* fall through */
2839 default:
2840 new = stack_putstr(p, new);
2841 USTPUTC('/', new);
2842 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002843 p = strtok_r(NULL, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002844 }
2845 if (new > lim)
2846 STUNPUTC(new);
2847 *new = 0;
2848 return stackblock();
2849}
2850
2851/*
2852 * Find out what the current directory is. If we already know the current
2853 * directory, this routine returns immediately.
2854 */
2855static char *
2856getpwd(void)
2857{
Denis Vlasenko01631112007-12-16 17:20:38 +00002858 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002859 return dir ? dir : nullstr;
2860}
2861
2862static void
2863setpwd(const char *val, int setold)
2864{
2865 char *oldcur, *dir;
2866
2867 oldcur = dir = curdir;
2868
2869 if (setold) {
2870 setvar("OLDPWD", oldcur, VEXPORT);
2871 }
2872 INT_OFF;
2873 if (physdir != nullstr) {
2874 if (physdir != oldcur)
2875 free(physdir);
2876 physdir = nullstr;
2877 }
2878 if (oldcur == val || !val) {
2879 char *s = getpwd();
2880 physdir = s;
2881 if (!val)
2882 dir = s;
2883 } else
2884 dir = ckstrdup(val);
2885 if (oldcur != dir && oldcur != nullstr) {
2886 free(oldcur);
2887 }
2888 curdir = dir;
2889 INT_ON;
2890 setvar("PWD", dir, VEXPORT);
2891}
2892
2893static void hashcd(void);
2894
2895/*
Denys Vlasenko70392332016-10-27 02:31:55 +02002896 * Actually do the chdir. We also call hashcd to let other routines
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002897 * know that the current directory has changed.
2898 */
2899static int
2900docd(const char *dest, int flags)
2901{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002902 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002903 int err;
2904
2905 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2906
2907 INT_OFF;
2908 if (!(flags & CD_PHYSICAL)) {
2909 dir = updatepwd(dest);
2910 if (dir)
2911 dest = dir;
2912 }
2913 err = chdir(dest);
2914 if (err)
2915 goto out;
2916 setpwd(dir, 1);
2917 hashcd();
2918 out:
2919 INT_ON;
2920 return err;
2921}
2922
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002923static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002924cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002925{
2926 const char *dest;
2927 const char *path;
2928 const char *p;
2929 char c;
2930 struct stat statb;
2931 int flags;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002932 int len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002933
2934 flags = cdopt();
2935 dest = *argptr;
2936 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002937 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002938 else if (LONE_DASH(dest)) {
2939 dest = bltinlookup("OLDPWD");
2940 flags |= CD_PRINT;
2941 }
2942 if (!dest)
2943 dest = nullstr;
2944 if (*dest == '/')
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002945 goto step6;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002946 if (*dest == '.') {
2947 c = dest[1];
2948 dotdot:
2949 switch (c) {
2950 case '\0':
2951 case '/':
2952 goto step6;
2953 case '.':
2954 c = dest[2];
2955 if (c != '.')
2956 goto dotdot;
2957 }
2958 }
2959 if (!*dest)
2960 dest = ".";
2961 path = bltinlookup("CDPATH");
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002962 while (p = path, (len = padvance(&path, dest)) >= 0) {
2963 c = *p;
2964 p = stalloc(len);
2965
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002966 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2967 if (c && c != ':')
2968 flags |= CD_PRINT;
2969 docd:
2970 if (!docd(p, flags))
2971 goto out;
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002972 goto err;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002973 }
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002974 }
2975
2976 step6:
2977 p = dest;
2978 goto docd;
2979
2980 err:
Johannes Schindelin687aac02017-08-22 22:03:22 +02002981 ash_msg_and_raise_perror("can't cd to %s", dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002982 /* NOTREACHED */
2983 out:
2984 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002985 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002986 return 0;
2987}
2988
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002989static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002990pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002991{
2992 int flags;
2993 const char *dir = curdir;
2994
2995 flags = cdopt();
2996 if (flags) {
2997 if (physdir == nullstr)
2998 setpwd(dir, 0);
2999 dir = physdir;
3000 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02003001 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00003002 return 0;
3003}
3004
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003005
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00003006/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00003007
Denis Vlasenko834dee72008-10-07 09:18:30 +00003008
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02003009#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00003010
Eric Andersenc470f442003-07-28 09:56:35 +00003011/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003012#define CWORD 0 /* character is nothing special */
3013#define CNL 1 /* newline character */
3014#define CBACK 2 /* a backslash character */
3015#define CSQUOTE 3 /* single quote */
3016#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00003017#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003018#define CBQUOTE 6 /* backwards single quote */
3019#define CVAR 7 /* a dollar sign */
3020#define CENDVAR 8 /* a '}' character */
3021#define CLP 9 /* a left paren in arithmetic */
3022#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00003023#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003024#define CCTL 12 /* like CWORD, except it must be escaped */
3025#define CSPCL 13 /* these terminate a word */
Eric Andersenc470f442003-07-28 09:56:35 +00003026
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003027#define PEOF 256
Eric Andersenc470f442003-07-28 09:56:35 +00003028
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003029#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003030
Denys Vlasenko0b883582016-12-23 16:49:07 +01003031#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003032# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00003033#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003034# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003035#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003036static const uint16_t S_I_T[] ALIGN2 = {
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003037 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 0, ' ' */
3038 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 1, \n */
3039 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 2, !*-/:=?[]~ */
3040 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 3, '"' */
3041 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 4, $ */
3042 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 5, "'" */
3043 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 6, ( */
3044 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 7, ) */
3045 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 8, \ */
3046 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 9, ` */
3047 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 10, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003048#if !USE_SIT_FUNCTION
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003049 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 11, PEOF */
3050 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 12, 0-9A-Za-z */
3051 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 13, CTLESC ... */
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003052#endif
3053#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00003054};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003055/* Constants below must match table above */
3056enum {
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003057 CSPCL_CWORD_CWORD_CWORD , /* 0 */
3058 CNL_CNL_CNL_CNL , /* 1 */
3059 CWORD_CCTL_CCTL_CWORD , /* 2 */
3060 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 3 */
3061 CVAR_CVAR_CWORD_CVAR , /* 4 */
3062 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 5 */
3063 CSPCL_CWORD_CWORD_CLP , /* 6 */
3064 CSPCL_CWORD_CWORD_CRP , /* 7 */
3065 CBACK_CBACK_CCTL_CBACK , /* 8 */
3066 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 9 */
3067 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 10 */
3068 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 11 */
3069 CWORD_CWORD_CWORD_CWORD , /* 12 */
3070 CCTL_CCTL_CCTL_CCTL , /* 13 */
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003071};
Eric Andersen2870d962001-07-02 17:27:21 +00003072
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003073/* c in SIT(c, syntax) must be an *unsigned char* or PEOF,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003074 * caller must ensure proper cast on it if c is *char_ptr!
3075 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003076#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003077
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003078static int
3079SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003080{
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003081 /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */
3082 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~";
3083 /*
3084 * This causes '/' to be prepended with CTLESC in dquoted string,
3085 * making "./file"* treated incorrectly because we feed
3086 * ".\/file*" string to glob(), confusing it (see expandmeta func).
3087 * The "homegrown" glob implementation is okay with that,
3088 * but glibc one isn't. With '/' always treated as CWORD,
3089 * both work fine.
3090 */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003091 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003092 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003093 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003094 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
3095 10, 2 /* "}~" */
3096 };
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003097 const char *s;
3098 int indx;
3099
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003100 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003101 return CENDFILE;
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003102 /* Cast is purely for paranoia here,
3103 * just in case someone passed signed char to us */
3104 if ((unsigned char)c >= CTL_FIRST
3105 && (unsigned char)c <= CTL_LAST
3106 ) {
3107 return CCTL;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003108 }
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003109 s = strchrnul(spec_symbls, c);
3110 if (*s == '\0')
3111 return CWORD;
3112 indx = syntax_index_table[s - spec_symbls];
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003113 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003114}
3115
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003116#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003117
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003118static const uint8_t syntax_index_table[] ALIGN1 = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003119 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003120 /* 0 */ CWORD_CWORD_CWORD_CWORD,
3121 /* 1 */ CWORD_CWORD_CWORD_CWORD,
3122 /* 2 */ CWORD_CWORD_CWORD_CWORD,
3123 /* 3 */ CWORD_CWORD_CWORD_CWORD,
3124 /* 4 */ CWORD_CWORD_CWORD_CWORD,
3125 /* 5 */ CWORD_CWORD_CWORD_CWORD,
3126 /* 6 */ CWORD_CWORD_CWORD_CWORD,
3127 /* 7 */ CWORD_CWORD_CWORD_CWORD,
3128 /* 8 */ CWORD_CWORD_CWORD_CWORD,
3129 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
3130 /* 10 "\n" */ CNL_CNL_CNL_CNL,
3131 /* 11 */ CWORD_CWORD_CWORD_CWORD,
3132 /* 12 */ CWORD_CWORD_CWORD_CWORD,
3133 /* 13 */ CWORD_CWORD_CWORD_CWORD,
3134 /* 14 */ CWORD_CWORD_CWORD_CWORD,
3135 /* 15 */ CWORD_CWORD_CWORD_CWORD,
3136 /* 16 */ CWORD_CWORD_CWORD_CWORD,
3137 /* 17 */ CWORD_CWORD_CWORD_CWORD,
3138 /* 18 */ CWORD_CWORD_CWORD_CWORD,
3139 /* 19 */ CWORD_CWORD_CWORD_CWORD,
3140 /* 20 */ CWORD_CWORD_CWORD_CWORD,
3141 /* 21 */ CWORD_CWORD_CWORD_CWORD,
3142 /* 22 */ CWORD_CWORD_CWORD_CWORD,
3143 /* 23 */ CWORD_CWORD_CWORD_CWORD,
3144 /* 24 */ CWORD_CWORD_CWORD_CWORD,
3145 /* 25 */ CWORD_CWORD_CWORD_CWORD,
3146 /* 26 */ CWORD_CWORD_CWORD_CWORD,
3147 /* 27 */ CWORD_CWORD_CWORD_CWORD,
3148 /* 28 */ CWORD_CWORD_CWORD_CWORD,
3149 /* 29 */ CWORD_CWORD_CWORD_CWORD,
3150 /* 30 */ CWORD_CWORD_CWORD_CWORD,
3151 /* 31 */ CWORD_CWORD_CWORD_CWORD,
3152 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
3153 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
3154 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
3155 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
3156 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
3157 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
3158 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
3159 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
3160 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
3161 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
3162 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
3163 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
3164 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
3165 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
3166 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003167/* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */
3168 /* 47 "/" */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003169 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
3170 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
3171 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
3172 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
3173 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
3174 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
3175 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
3176 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
3177 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
3178 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
3179 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
3180 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
3181 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
3182 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
3183 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
3184 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
3185 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
3186 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
3187 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
3188 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
3189 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
3190 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
3191 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
3192 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
3193 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
3194 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
3195 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
3196 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
3197 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
3198 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
3199 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
3200 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
3201 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
3202 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
3203 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
3204 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
3205 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
3206 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
3207 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
3208 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
3209 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
3210 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
3211 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
3212 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
3213 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
3214 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
3215 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
3216 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
3217 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
3218 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
3219 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
3220 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
3221 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
3222 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
3223 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
3224 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
3225 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
3226 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
3227 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
3228 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
3229 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
3230 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3231 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3232 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3233 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3234 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3235 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3236 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3237 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3238 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3239 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3240 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3241 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3242 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3243 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3244 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3245 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3246 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3247 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3248 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
3249 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
3250 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
3251 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
3252 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
3253 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
3254 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
3255 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
3256 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
3257 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Ron Yorstona1b0d382020-07-23 08:32:27 +01003258#if BASH_PROCESS_SUBST
3259 /* 137 CTLTOPROC */ CCTL_CCTL_CCTL_CCTL,
3260 /* 138 CTLFROMPROC */ CCTL_CCTL_CCTL_CCTL,
3261#else
Denys Vlasenkocd716832009-11-28 22:14:02 +01003262 /* 137 */ CWORD_CWORD_CWORD_CWORD,
3263 /* 138 */ CWORD_CWORD_CWORD_CWORD,
Ron Yorstona1b0d382020-07-23 08:32:27 +01003264#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01003265 /* 139 */ CWORD_CWORD_CWORD_CWORD,
3266 /* 140 */ CWORD_CWORD_CWORD_CWORD,
3267 /* 141 */ CWORD_CWORD_CWORD_CWORD,
3268 /* 142 */ CWORD_CWORD_CWORD_CWORD,
3269 /* 143 */ CWORD_CWORD_CWORD_CWORD,
3270 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3271 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3272 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3273 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3274 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3275 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3276 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3277 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3278 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3279 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3280 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3281 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3282 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3283 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3284 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3285 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3286 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3287 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3288 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3289 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3290 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3291 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3292 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3293 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3294 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3295 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3296 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3297 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3298 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3299 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3300 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3301 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3302 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3303 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3304 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3305 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3306 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3307 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3308 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3309 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3310 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3311 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3312 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3313 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3314 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3315 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3316 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3317 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3318 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3319 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3320 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3321 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3322 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3323 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3324 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3325 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3326 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3327 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3328 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3329 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3330 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3331 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3332 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3333 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3334 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3335 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3336 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3337 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3338 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3339 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3340 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3341 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3342 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3343 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3344 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3345 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3346 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3347 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3348 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3349 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3350 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3351 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3352 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3353 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3354 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3355 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3356 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3357 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3358 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3359 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3360 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3361 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3362 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3363 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3364 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3365 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3366 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3367 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3368 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3369 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3370 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3371 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3372 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3373 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3374 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3375 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3376 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3377 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3378 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3379 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3380 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3381 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003382 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Eric Andersen2870d962001-07-02 17:27:21 +00003383};
3384
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003385#if 1
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003386# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003387#else /* debug version, caught one signed char bug */
3388# define SIT(c, syntax) \
3389 ({ \
3390 if ((c) < 0 || (c) > (PEOF + ENABLE_ASH_ALIAS)) \
3391 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
Denys Vlasenko0b883582016-12-23 16:49:07 +01003392 if ((syntax) < 0 || (syntax) > (2 + ENABLE_FEATURE_SH_MATH)) \
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003393 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
3394 ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf); \
3395 })
3396#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003397
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003398#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003399
Eric Andersen2870d962001-07-02 17:27:21 +00003400
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003401/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003402
Denis Vlasenko131ae172007-02-18 13:00:19 +00003403#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003404
3405#define ALIASINUSE 1
3406#define ALIASDEAD 2
3407
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003408struct alias {
3409 struct alias *next;
3410 char *name;
3411 char *val;
3412 int flag;
3413};
3414
Denis Vlasenko01631112007-12-16 17:20:38 +00003415
3416static struct alias **atab; // [ATABSIZE];
3417#define INIT_G_alias() do { \
3418 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3419} while (0)
3420
Eric Andersen2870d962001-07-02 17:27:21 +00003421
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003422static struct alias **
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02003423__lookupalias(const char *name)
3424{
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003425 unsigned int hashval;
3426 struct alias **app;
3427 const char *p;
3428 unsigned int ch;
3429
3430 p = name;
3431
3432 ch = (unsigned char)*p;
3433 hashval = ch << 4;
3434 while (ch) {
3435 hashval += ch;
3436 ch = (unsigned char)*++p;
3437 }
3438 app = &atab[hashval % ATABSIZE];
3439
3440 for (; *app; app = &(*app)->next) {
3441 if (strcmp(name, (*app)->name) == 0) {
3442 break;
3443 }
3444 }
3445
3446 return app;
3447}
3448
3449static struct alias *
3450lookupalias(const char *name, int check)
3451{
3452 struct alias *ap = *__lookupalias(name);
3453
3454 if (check && ap && (ap->flag & ALIASINUSE))
3455 return NULL;
3456 return ap;
3457}
3458
3459static struct alias *
3460freealias(struct alias *ap)
3461{
3462 struct alias *next;
3463
3464 if (ap->flag & ALIASINUSE) {
3465 ap->flag |= ALIASDEAD;
3466 return ap;
3467 }
3468
3469 next = ap->next;
3470 free(ap->name);
3471 free(ap->val);
3472 free(ap);
3473 return next;
3474}
Eric Andersencb57d552001-06-28 07:25:16 +00003475
Eric Andersenc470f442003-07-28 09:56:35 +00003476static void
3477setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003478{
3479 struct alias *ap, **app;
3480
3481 app = __lookupalias(name);
3482 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003483 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003484 if (ap) {
3485 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003486 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003487 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003488 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003489 ap->flag &= ~ALIASDEAD;
3490 } else {
3491 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003492 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003493 ap->name = ckstrdup(name);
3494 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003495 /*ap->flag = 0; - ckzalloc did it */
3496 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003497 *app = ap;
3498 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003499 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003500}
3501
Eric Andersenc470f442003-07-28 09:56:35 +00003502static int
3503unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003504{
Eric Andersencb57d552001-06-28 07:25:16 +00003505 struct alias **app;
3506
3507 app = __lookupalias(name);
3508
3509 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003510 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003511 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003512 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003513 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003514 }
3515
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003516 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003517}
3518
Eric Andersenc470f442003-07-28 09:56:35 +00003519static void
3520rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003521{
Eric Andersencb57d552001-06-28 07:25:16 +00003522 struct alias *ap, **app;
3523 int i;
3524
Denis Vlasenkob012b102007-02-19 22:43:01 +00003525 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003526 for (i = 0; i < ATABSIZE; i++) {
3527 app = &atab[i];
3528 for (ap = *app; ap; ap = *app) {
3529 *app = freealias(*app);
3530 if (ap == *app) {
3531 app = &ap->next;
3532 }
3533 }
3534 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003535 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003536}
3537
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003538static void
3539printalias(const struct alias *ap)
3540{
3541 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3542}
3543
Eric Andersencb57d552001-06-28 07:25:16 +00003544/*
3545 * TODO - sort output
3546 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003547static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003548aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003549{
3550 char *n, *v;
3551 int ret = 0;
3552 struct alias *ap;
3553
Denis Vlasenko68404f12008-03-17 09:00:54 +00003554 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003555 int i;
3556
Denis Vlasenko68404f12008-03-17 09:00:54 +00003557 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003558 for (ap = atab[i]; ap; ap = ap->next) {
3559 printalias(ap);
3560 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003561 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003562 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003563 }
3564 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003565 v = strchr(n+1, '=');
3566 if (v == NULL) { /* n+1: funny ksh stuff */
3567 ap = *__lookupalias(n);
3568 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003569 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003570 ret = 1;
3571 } else
3572 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003573 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003574 *v++ = '\0';
3575 setalias(n, v);
3576 }
3577 }
3578
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003579 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003580}
3581
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003582static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003583unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003584{
3585 int i;
3586
Denys Vlasenko6c149f42017-04-12 21:31:32 +02003587 while (nextopt("a") != '\0') {
3588 rmaliases();
3589 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003590 }
3591 for (i = 0; *argptr; argptr++) {
3592 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003593 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003594 i = 1;
3595 }
3596 }
3597
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003598 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003599}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003600
Denis Vlasenko131ae172007-02-18 13:00:19 +00003601#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003602
Eric Andersenc470f442003-07-28 09:56:35 +00003603
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003604/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003605#define FORK_FG 0
3606#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003607#define FORK_NOJOB 2
3608
3609/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003610#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3611#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3612#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denys Vlasenko9c541002015-10-07 15:44:36 +02003613#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003614
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003615/*
3616 * A job structure contains information about a job. A job is either a
3617 * single process or a set of processes contained in a pipeline. In the
3618 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3619 * array of pids.
3620 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003621struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003622 pid_t ps_pid; /* process id */
3623 int ps_status; /* last process status from wait() */
3624 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003625};
3626
3627struct job {
3628 struct procstat ps0; /* status of process */
Denys Vlasenko966f0872019-03-27 15:51:42 +01003629 struct procstat *ps; /* status of processes when more than one */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003630#if JOBS
3631 int stopstatus; /* status of a stopped job */
3632#endif
Denys Vlasenko4c179372017-01-11 18:44:15 +01003633 unsigned nprocs; /* number of processes */
3634
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003635#define JOBRUNNING 0 /* at least one proc running */
3636#define JOBSTOPPED 1 /* all procs are stopped */
3637#define JOBDONE 2 /* all procs are completed */
Denys Vlasenko4c179372017-01-11 18:44:15 +01003638 unsigned
3639 state: 8,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003640#if JOBS
3641 sigint: 1, /* job was killed by SIGINT */
3642 jobctl: 1, /* job running under job control */
3643#endif
3644 waited: 1, /* true if this entry has been waited for */
3645 used: 1, /* true if this entry is in used */
3646 changed: 1; /* true if status has changed */
3647 struct job *prev_job; /* previous job */
3648};
3649
Denis Vlasenko68404f12008-03-17 09:00:54 +00003650static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003651static int forkshell(struct job *, union node *, int);
3652static int waitforjob(struct job *);
3653
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003654#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003655enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003656#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003657#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003658static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003659static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003660#endif
3661
3662/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003663 * Ignore a signal.
3664 */
3665static void
3666ignoresig(int signo)
3667{
3668 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3669 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3670 /* No, need to do it */
3671 signal(signo, SIG_IGN);
3672 }
3673 sigmode[signo - 1] = S_HARD_IGN;
3674}
3675
3676/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003677 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003678 */
3679static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003680signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003681{
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003682 if (signo == SIGCHLD) {
3683 got_sigchld = 1;
3684 if (!trap[SIGCHLD])
3685 return;
3686 }
Denys Vlasenko12566e72022-01-17 03:02:40 +01003687#if ENABLE_FEATURE_EDITING
3688 bb_got_signal = signo; /* for read_line_input: "we got a signal" */
3689#endif
Denis Vlasenko4b875702009-03-19 13:30:04 +00003690 gotsig[signo - 1] = 1;
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003691 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003692
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003693 if (signo == SIGINT && !trap[SIGINT]) {
3694 if (!suppress_int) {
3695 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003696 raise_interrupt(); /* does not return */
3697 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003698 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003699 }
3700}
3701
3702/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003703 * Set the signal handler for the specified signal. The routine figures
3704 * out what it should be set to.
3705 */
3706static void
3707setsignal(int signo)
3708{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003709 char *t;
3710 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003711 struct sigaction act;
3712
3713 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003714 new_act = S_DFL;
3715 if (t != NULL) { /* trap for this sig is set */
3716 new_act = S_CATCH;
3717 if (t[0] == '\0') /* trap is "": ignore this sig */
3718 new_act = S_IGN;
3719 }
3720
3721 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003722 switch (signo) {
3723 case SIGINT:
3724 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003725 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003726 break;
3727 case SIGQUIT:
3728#if DEBUG
3729 if (debug)
3730 break;
3731#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003732 /* man bash:
3733 * "In all cases, bash ignores SIGQUIT. Non-builtin
3734 * commands run by bash have signal handlers
3735 * set to the values inherited by the shell
3736 * from its parent". */
3737 new_act = S_IGN;
3738 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003739 case SIGTERM:
3740 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003741 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003742 break;
3743#if JOBS
3744 case SIGTSTP:
3745 case SIGTTOU:
3746 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003747 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003748 break;
3749#endif
3750 }
3751 }
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003752 /* if !rootshell, we reset SIGQUIT to DFL,
3753 * whereas we have to restore it to what shell got on entry.
3754 * This is handled by the fact that if signal was IGNored on entry,
3755 * then cur_act is S_HARD_IGN and we never change its sigaction
3756 * (see code below).
3757 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003758
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003759 if (signo == SIGCHLD)
3760 new_act = S_CATCH;
3761
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003762 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003763 cur_act = *t;
3764 if (cur_act == 0) {
3765 /* current setting is not yet known */
3766 if (sigaction(signo, NULL, &act)) {
3767 /* pretend it worked; maybe we should give a warning,
3768 * but other shells don't. We don't alter sigmode,
3769 * so we retry every time.
3770 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003771 return;
3772 }
3773 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003774 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003775 if (mflag
3776 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3777 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003778 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003779 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780 }
Denys Vlasenko0f14f412017-08-06 20:06:19 +02003781 if (act.sa_handler == SIG_DFL && new_act == S_DFL) {
3782 /* installing SIG_DFL over SIG_DFL is a no-op */
3783 /* saves one sigaction call in each "sh -c SCRIPT" invocation */
3784 *t = S_DFL;
3785 return;
3786 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003787 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003788 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003789 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003790
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003791 *t = new_act;
3792
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003793 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003794 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003796 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003797 break;
3798 case S_IGN:
3799 act.sa_handler = SIG_IGN;
3800 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003801 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003802 /* flags and mask matter only if !DFL and !IGN, but we do it
3803 * for all cases for more deterministic behavior:
3804 */
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003805 act.sa_flags = 0; //TODO: why not SA_RESTART?
Ian Wienand89b3cba2011-04-16 20:05:14 +02003806 sigfillset(&act.sa_mask);
3807
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003808 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003809}
3810
3811/* mode flags for set_curjob */
3812#define CUR_DELETE 2
3813#define CUR_RUNNING 1
3814#define CUR_STOPPED 0
3815
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003816#if JOBS
3817/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003818static int initialpgrp; //references:2
3819static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003820#endif
3821/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003822static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003823/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003824static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003825/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003826static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003827
Denys Vlasenko098b7132017-01-11 19:59:03 +01003828#if 0
3829/* Bash has a feature: it restores termios after a successful wait for
3830 * a foreground job which had at least one stopped or sigkilled member.
3831 * The probable rationale is that SIGSTOP and SIGKILL can preclude task from
3832 * properly restoring tty state. Should we do this too?
3833 * A reproducer: ^Z an interactive python:
3834 *
3835 * # python
3836 * Python 2.7.12 (...)
3837 * >>> ^Z
3838 * { python leaves tty in -icanon -echo state. We do survive that... }
3839 * [1]+ Stopped python
3840 * { ...however, next program (python #2) does not survive it well: }
3841 * # python
3842 * Python 2.7.12 (...)
3843 * >>> Traceback (most recent call last):
3844 * { above, I typed "qwerty<CR>", but -echo state is still in effect }
3845 * File "<stdin>", line 1, in <module>
3846 * NameError: name 'qwerty' is not defined
3847 *
3848 * The implementation below is modeled on bash code and seems to work.
3849 * However, I'm not sure we should do this. For one: what if I'd fg
3850 * the stopped python instead? It'll be confused by "restored" tty state.
3851 */
3852static struct termios shell_tty_info;
3853static void
3854get_tty_state(void)
3855{
3856 if (rootshell && ttyfd >= 0)
3857 tcgetattr(ttyfd, &shell_tty_info);
3858}
3859static void
3860set_tty_state(void)
3861{
3862 /* if (rootshell) - caller ensures this */
3863 if (ttyfd >= 0)
3864 tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
3865}
3866static int
3867job_signal_status(struct job *jp)
3868{
3869 int status;
3870 unsigned i;
3871 struct procstat *ps = jp->ps;
3872 for (i = 0; i < jp->nprocs; i++) {
3873 status = ps[i].ps_status;
3874 if (WIFSIGNALED(status) || WIFSTOPPED(status))
3875 return status;
3876 }
3877 return 0;
3878}
3879static void
3880restore_tty_if_stopped_or_signaled(struct job *jp)
3881{
3882//TODO: check what happens if we come from waitforjob() in expbackq()
3883 if (rootshell) {
3884 int s = job_signal_status(jp);
3885 if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
3886 set_tty_state();
3887 }
3888}
3889#else
3890# define get_tty_state() ((void)0)
3891# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
3892#endif
3893
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003894static void
3895set_curjob(struct job *jp, unsigned mode)
3896{
3897 struct job *jp1;
3898 struct job **jpp, **curp;
3899
3900 /* first remove from list */
3901 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003902 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003903 jp1 = *jpp;
3904 if (jp1 == jp)
3905 break;
3906 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003907 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003908 *jpp = jp1->prev_job;
3909
3910 /* Then re-insert in correct position */
3911 jpp = curp;
3912 switch (mode) {
3913 default:
3914#if DEBUG
3915 abort();
3916#endif
3917 case CUR_DELETE:
3918 /* job being deleted */
3919 break;
3920 case CUR_RUNNING:
3921 /* newly created job or backgrounded job,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +01003922 * put after all stopped jobs.
3923 */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003924 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003925 jp1 = *jpp;
3926#if JOBS
3927 if (!jp1 || jp1->state != JOBSTOPPED)
3928#endif
3929 break;
3930 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003931 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932 /* FALLTHROUGH */
3933#if JOBS
3934 case CUR_STOPPED:
3935#endif
3936 /* newly stopped job - becomes curjob */
3937 jp->prev_job = *jpp;
3938 *jpp = jp;
3939 break;
3940 }
3941}
3942
3943#if JOBS || DEBUG
3944static int
3945jobno(const struct job *jp)
3946{
3947 return jp - jobtab + 1;
3948}
3949#endif
3950
3951/*
3952 * Convert a job name to a job structure.
3953 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003954#if !JOBS
3955#define getjob(name, getctl) getjob(name)
3956#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957static struct job *
3958getjob(const char *name, int getctl)
3959{
3960 struct job *jp;
3961 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003962 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003963 unsigned num;
3964 int c;
3965 const char *p;
3966 char *(*match)(const char *, const char *);
3967
3968 jp = curjob;
3969 p = name;
3970 if (!p)
3971 goto currentjob;
3972
3973 if (*p != '%')
3974 goto err;
3975
3976 c = *++p;
3977 if (!c)
3978 goto currentjob;
3979
3980 if (!p[1]) {
3981 if (c == '+' || c == '%') {
3982 currentjob:
3983 err_msg = "No current job";
3984 goto check;
3985 }
3986 if (c == '-') {
3987 if (jp)
3988 jp = jp->prev_job;
3989 err_msg = "No previous job";
3990 check:
3991 if (!jp)
3992 goto err;
3993 goto gotit;
3994 }
3995 }
3996
3997 if (is_number(p)) {
3998 num = atoi(p);
Denys Vlasenko46a45ce2016-09-29 01:10:08 +02003999 if (num > 0 && num <= njobs) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004000 jp = jobtab + num - 1;
4001 if (jp->used)
4002 goto gotit;
4003 goto err;
4004 }
4005 }
4006
4007 match = prefix;
4008 if (*p == '?') {
4009 match = strstr;
4010 p++;
4011 }
4012
Denys Vlasenkoffc39202009-08-17 02:12:20 +02004013 found = NULL;
4014 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004015 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004016 if (found)
4017 goto err;
4018 found = jp;
4019 err_msg = "%s: ambiguous";
4020 }
4021 jp = jp->prev_job;
4022 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02004023 if (!found)
4024 goto err;
4025 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004026
4027 gotit:
4028#if JOBS
4029 err_msg = "job %s not created under job control";
4030 if (getctl && jp->jobctl == 0)
4031 goto err;
4032#endif
4033 return jp;
4034 err:
4035 ash_msg_and_raise_error(err_msg, name);
4036}
4037
4038/*
4039 * Mark a job structure as unused.
4040 */
4041static void
4042freejob(struct job *jp)
4043{
4044 struct procstat *ps;
4045 int i;
4046
4047 INT_OFF;
4048 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004049 if (ps->ps_cmd != nullstr)
4050 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051 }
4052 if (jp->ps != &jp->ps0)
4053 free(jp->ps);
4054 jp->used = 0;
4055 set_curjob(jp, CUR_DELETE);
4056 INT_ON;
4057}
4058
4059#if JOBS
4060static void
4061xtcsetpgrp(int fd, pid_t pgrp)
4062{
4063 if (tcsetpgrp(fd, pgrp))
Ron Yorstonbe366e52017-07-27 13:53:39 +01004064 ash_msg_and_raise_perror("can't set tty process group");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004065}
4066
4067/*
4068 * Turn job control on and off.
4069 *
4070 * Note: This code assumes that the third arg to ioctl is a character
4071 * pointer, which is true on Berkeley systems but not System V. Since
4072 * System V doesn't have job control yet, this isn't a problem now.
4073 *
4074 * Called with interrupts off.
4075 */
4076static void
4077setjobctl(int on)
4078{
4079 int fd;
4080 int pgrp;
4081
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004082 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004083 return;
4084 if (on) {
4085 int ofd;
4086 ofd = fd = open(_PATH_TTY, O_RDWR);
4087 if (fd < 0) {
4088 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
4089 * That sometimes helps to acquire controlling tty.
4090 * Obviously, a workaround for bugs when someone
4091 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004092 fd = 2;
4093 while (!isatty(fd))
4094 if (--fd < 0)
4095 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 }
Denys Vlasenko64774602016-10-26 15:24:30 +02004097 /* fd is a tty at this point */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004098 fd = fcntl(fd, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko10ad6222017-04-17 16:13:32 +02004099 if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, don't */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004100 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004101 if (fd < 0)
Denys Vlasenko64774602016-10-26 15:24:30 +02004102 goto out; /* F_DUPFD failed */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004103 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
4104 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004105 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004106 pgrp = tcgetpgrp(fd);
4107 if (pgrp < 0) {
4108 out:
4109 ash_msg("can't access tty; job control turned off");
4110 mflag = on = 0;
4111 goto close;
4112 }
4113 if (pgrp == getpgrp())
4114 break;
4115 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004116 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004117 initialpgrp = pgrp;
4118
4119 setsignal(SIGTSTP);
4120 setsignal(SIGTTOU);
4121 setsignal(SIGTTIN);
4122 pgrp = rootpid;
4123 setpgid(0, pgrp);
4124 xtcsetpgrp(fd, pgrp);
4125 } else {
4126 /* turning job control off */
4127 fd = ttyfd;
4128 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004129 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004130 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004131 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004132 setpgid(0, pgrp);
4133 setsignal(SIGTSTP);
4134 setsignal(SIGTTOU);
4135 setsignal(SIGTTIN);
4136 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004137 if (fd >= 0)
4138 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004139 fd = -1;
4140 }
4141 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004142 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004143}
4144
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004145static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004146killcmd(int argc, char **argv)
4147{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004148 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004149 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004150 do {
4151 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004152 /*
4153 * "kill %N" - job kill
4154 * Converting to pgrp / pid kill
4155 */
4156 struct job *jp;
4157 char *dst;
4158 int j, n;
4159
4160 jp = getjob(argv[i], 0);
4161 /*
4162 * In jobs started under job control, we signal
4163 * entire process group by kill -PGRP_ID.
4164 * This happens, f.e., in interactive shell.
4165 *
4166 * Otherwise, we signal each child via
4167 * kill PID1 PID2 PID3.
4168 * Testcases:
4169 * sh -c 'sleep 1|sleep 1 & kill %1'
4170 * sh -c 'true|sleep 2 & sleep 1; kill %1'
4171 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4172 */
4173 n = jp->nprocs; /* can't be 0 (I hope) */
4174 if (jp->jobctl)
4175 n = 1;
4176 dst = alloca(n * sizeof(int)*4);
4177 argv[i] = dst;
4178 for (j = 0; j < n; j++) {
4179 struct procstat *ps = &jp->ps[j];
4180 /* Skip non-running and not-stopped members
4181 * (i.e. dead members) of the job
4182 */
4183 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
4184 continue;
4185 /*
4186 * kill_main has matching code to expect
4187 * leading space. Needed to not confuse
4188 * negative pids with "kill -SIGNAL_NO" syntax
4189 */
4190 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4191 }
4192 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004193 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004194 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004195 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004196 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004197}
4198
4199static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01004200showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004201{
Denys Vlasenko285ad152009-12-04 23:02:27 +01004202 struct procstat *ps;
4203 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204
Denys Vlasenko285ad152009-12-04 23:02:27 +01004205 psend = jp->ps + jp->nprocs;
4206 for (ps = jp->ps + 1; ps < psend; ps++)
4207 printf(" | %s", ps->ps_cmd);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004208 newline_and_flush(stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004209 flush_stdout_stderr();
4210}
4211
4212
4213static int
4214restartjob(struct job *jp, int mode)
4215{
4216 struct procstat *ps;
4217 int i;
4218 int status;
4219 pid_t pgid;
4220
4221 INT_OFF;
4222 if (jp->state == JOBDONE)
4223 goto out;
4224 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004225 pgid = jp->ps[0].ps_pid;
Denys Vlasenko098b7132017-01-11 19:59:03 +01004226 if (mode == FORK_FG) {
4227 get_tty_state();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004228 xtcsetpgrp(ttyfd, pgid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01004229 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004230 killpg(pgid, SIGCONT);
4231 ps = jp->ps;
4232 i = jp->nprocs;
4233 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004234 if (WIFSTOPPED(ps->ps_status)) {
4235 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004236 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004237 ps++;
4238 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004239 out:
4240 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4241 INT_ON;
4242 return status;
4243}
4244
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004245static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004246fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004247{
4248 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004249 int mode;
4250 int retval;
4251
4252 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
4253 nextopt(nullstr);
4254 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004255 do {
4256 jp = getjob(*argv, 1);
4257 if (mode == FORK_BG) {
4258 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004259 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004260 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004261 out1str(jp->ps[0].ps_cmd);
4262 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004263 retval = restartjob(jp, mode);
4264 } while (*argv && *++argv);
4265 return retval;
4266}
4267#endif
4268
4269static int
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004270sprint_status48(char *os, int status, int sigonly)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004271{
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004272 char *s = os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004273 int st;
4274
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004275 if (!WIFEXITED(status)) {
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004276#if JOBS
4277 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004278 st = WSTOPSIG(status);
4279 else
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004280#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004281 st = WTERMSIG(status);
4282 if (sigonly) {
4283 if (st == SIGINT || st == SIGPIPE)
4284 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004285#if JOBS
4286 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004287 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004288#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004289 }
4290 st &= 0x7f;
Denys Vlasenko33745b12021-02-18 13:44:27 +01004291 s = stpncpy(s, strsignal(st), 32);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004292 if (WCOREDUMP(status)) {
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004293 s = stpcpy(s, " (core dumped)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004294 }
4295 } else if (!sigonly) {
4296 st = WEXITSTATUS(status);
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004297 s += fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004298 }
4299 out:
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004300 return s - os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004301}
4302
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004303#define DOWAIT_NONBLOCK 0
4304#define DOWAIT_BLOCK 1
4305#define DOWAIT_BLOCK_OR_SIG 2
Ron Yorstone48559e2019-03-31 09:27:09 +01004306#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004307# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4308#endif
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004309
4310static int
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004311waitproc(int block, int *status)
4312{
4313 sigset_t oldmask;
4314 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4315 int err;
4316
4317#if JOBS
4318 if (doing_jobctl)
4319 flags |= WUNTRACED;
4320#endif
4321
4322 do {
4323 got_sigchld = 0;
4324 do
4325 err = waitpid(-1, status, flags);
4326 while (err < 0 && errno == EINTR);
4327
4328 if (err || (err = -!block))
4329 break;
4330
4331 sigfillset(&oldmask);
4332 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4333 while (!got_sigchld && !pending_sig)
4334 sigsuspend(&oldmask);
4335 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4336 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4337 //while (!got_sigchld && !pending_sig)
4338 // pause();
4339
4340 } while (got_sigchld);
4341
4342 return err;
4343}
4344
4345static int
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004346waitone(int block, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004347{
4348 int pid;
4349 int status;
4350 struct job *jp;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004351 struct job *thisjob = NULL;
Ron Yorstone48559e2019-03-31 09:27:09 +01004352#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004353 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
4354 block = (block & ~DOWAIT_JOBSTATUS);
4355#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004356
Denys Vlasenkob543bda2016-10-27 20:08:28 +02004357 TRACE(("dowait(0x%x) called\n", block));
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00004358
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004359 /* It's wrong to call waitpid() outside of INT_OFF region:
4360 * signal can arrive just after syscall return and handler can
4361 * longjmp away, losing stop/exit notification processing.
4362 * Thus, for "jobs" builtin, and for waiting for a fg job,
4363 * we call waitpid() (blocking or non-blocking) inside INT_OFF.
4364 *
4365 * However, for "wait" builtin it is wrong to simply call waitpid()
4366 * in INT_OFF region: "wait" needs to wait for any running job
4367 * to change state, but should exit on any trap too.
4368 * In INT_OFF region, a signal just before syscall entry can set
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004369 * pending_sig variables, but we can't check them, and we would
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004370 * either enter a sleeping waitpid() (BUG), or need to busy-loop.
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004371 *
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004372 * Because of this, we run inside INT_OFF, but use a special routine
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004373 * which combines waitpid() and sigsuspend().
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004374 * This is the reason why we need to have a handler for SIGCHLD:
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004375 * SIG_DFL handler does not wake sigsuspend().
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004376 */
4377 INT_OFF;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004378 pid = waitproc(block, &status);
4379 TRACE(("wait returns pid %d, status=%d\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004380 if (pid <= 0)
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004381 goto out;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004382
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004383 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004384 int jobstate;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004385 struct procstat *ps;
4386 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 if (jp->state == JOBDONE)
4388 continue;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004389 jobstate = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004390 ps = jp->ps;
4391 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004392 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004393 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004394 TRACE(("Job %d: changing status of proc %d "
4395 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01004396 jobno(jp), pid, ps->ps_status, status));
4397 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004398 thisjob = jp;
4399 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004400 if (ps->ps_status == -1)
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004401 jobstate = JOBRUNNING;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004402#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004403 if (jobstate == JOBRUNNING)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004404 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004405 if (WIFSTOPPED(ps->ps_status)) {
4406 jp->stopstatus = ps->ps_status;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004407 jobstate = JOBSTOPPED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004408 }
4409#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01004410 } while (++ps < psend);
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004411 if (!thisjob)
4412 continue;
4413
4414 /* Found the job where one of its processes changed its state.
4415 * Is there at least one live and running process in this job? */
4416 if (jobstate != JOBRUNNING) {
4417 /* No. All live processes in the job are stopped
4418 * (JOBSTOPPED) or there are no live processes (JOBDONE)
4419 */
4420 thisjob->changed = 1;
4421 if (thisjob->state != jobstate) {
4422 TRACE(("Job %d: changing state from %d to %d\n",
4423 jobno(thisjob), thisjob->state, jobstate));
4424 thisjob->state = jobstate;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004425#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004426 if (jobstate == JOBSTOPPED)
4427 set_curjob(thisjob, CUR_STOPPED);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004428#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004429 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004430 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004431 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004432 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004433 /* The process wasn't found in job list */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004434 out:
4435 INT_ON;
4436
Ron Yorstone48559e2019-03-31 09:27:09 +01004437#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004438 if (want_jobexitstatus) {
4439 pid = -1;
4440 if (thisjob && thisjob->state == JOBDONE)
4441 pid = thisjob->ps[thisjob->nprocs - 1].ps_status;
4442 }
4443#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004444 if (thisjob && thisjob == job) {
4445 char s[48 + 1];
4446 int len;
4447
Denys Vlasenko9c541002015-10-07 15:44:36 +02004448 len = sprint_status48(s, status, 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004449 if (len) {
4450 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004451 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004452 out2str(s);
4453 }
4454 }
4455 return pid;
4456}
4457
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004458static int
4459dowait(int block, struct job *jp)
4460{
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004461 smallint gotchld = *(volatile smallint *)&got_sigchld;
4462 int rpid;
4463 int pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004464
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004465 if (jp && jp->state != JOBRUNNING)
4466 block = DOWAIT_NONBLOCK;
4467
4468 if (block == DOWAIT_NONBLOCK && !gotchld)
4469 return 1;
4470
4471 rpid = 1;
4472
4473 do {
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004474 pid = waitone(block, jp);
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004475 rpid &= !!pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004476
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004477 if (!pid || (jp && jp->state != JOBRUNNING))
4478 block = DOWAIT_NONBLOCK;
4479 } while (pid >= 0);
4480
4481 return rpid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004482}
4483
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004484#if JOBS
4485static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004486showjob(struct job *jp, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004487{
4488 struct procstat *ps;
4489 struct procstat *psend;
4490 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004491 int indent_col;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004492 char s[16 + 16 + 48];
4493 FILE *out = (mode & SHOW_STDERR ? stderr : stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004494
4495 ps = jp->ps;
4496
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004497 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004498 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004499 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004500 return;
4501 }
4502
4503 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004504 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004505
4506 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004507 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004508 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004509 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004510
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004511 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004512 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004513
4514 psend = ps + jp->nprocs;
4515
4516 if (jp->state == JOBRUNNING) {
4517 strcpy(s + col, "Running");
4518 col += sizeof("Running") - 1;
4519 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004520 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004521 if (jp->state == JOBSTOPPED)
4522 status = jp->stopstatus;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004523 col += sprint_status48(s + col, status, 0);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004524 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004525 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004526
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004527 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4528 * or prints several "PID | <cmdN>" lines,
4529 * depending on SHOW_PIDS bit.
4530 * We do not print status of individual processes
4531 * between PID and <cmdN>. bash does it, but not very well:
4532 * first line shows overall job status, not process status,
4533 * making it impossible to know 1st process status.
4534 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004535 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004536 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004538 s[0] = '\0';
4539 col = 33;
4540 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004541 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004542 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004543 fprintf(out, "%s%*c%s%s",
4544 s,
4545 33 - col >= 0 ? 33 - col : 0, ' ',
4546 ps == jp->ps ? "" : "| ",
4547 ps->ps_cmd
4548 );
4549 } while (++ps != psend);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004550 newline_and_flush(out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004551
4552 jp->changed = 0;
4553
4554 if (jp->state == JOBDONE) {
4555 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4556 freejob(jp);
4557 }
4558}
4559
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004560/*
4561 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4562 * statuses have changed since the last call to showjobs.
4563 */
4564static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004565showjobs(int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004566{
4567 struct job *jp;
4568
Denys Vlasenko883cea42009-07-11 15:31:59 +02004569 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004570
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004571 /* Handle all finished jobs */
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004572 dowait(DOWAIT_NONBLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004573
4574 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004575 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004576 showjob(jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004577 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004578 }
4579}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004580
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004581static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004582jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004583{
4584 int mode, m;
4585
4586 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004587 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004588 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004589 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004590 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004591 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004592 }
4593
4594 argv = argptr;
4595 if (*argv) {
4596 do
Denys Vlasenko9c541002015-10-07 15:44:36 +02004597 showjob(getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004598 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004599 } else {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004600 showjobs(mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004601 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004602
4603 return 0;
4604}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004605#endif /* JOBS */
4606
Michael Abbott359da5e2009-12-04 23:03:29 +01004607/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004608static int
4609getstatus(struct job *job)
4610{
4611 int status;
4612 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004613 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004614
Michael Abbott359da5e2009-12-04 23:03:29 +01004615 /* Fetch last member's status */
4616 ps = job->ps + job->nprocs - 1;
4617 status = ps->ps_status;
4618 if (pipefail) {
4619 /* "set -o pipefail" mode: use last _nonzero_ status */
4620 while (status == 0 && --ps >= job->ps)
4621 status = ps->ps_status;
4622 }
4623
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004624 retval = WEXITSTATUS(status);
4625 if (!WIFEXITED(status)) {
4626#if JOBS
4627 retval = WSTOPSIG(status);
4628 if (!WIFSTOPPED(status))
4629#endif
4630 {
4631 /* XXX: limits number of signals */
4632 retval = WTERMSIG(status);
4633#if JOBS
4634 if (retval == SIGINT)
4635 job->sigint = 1;
4636#endif
4637 }
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004638 retval |= 128;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004639 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004640 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004641 jobno(job), job->nprocs, status, retval));
4642 return retval;
4643}
4644
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004645static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004646waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004647{
4648 struct job *job;
4649 int retval;
4650 struct job *jp;
Ron Yorstone48559e2019-03-31 09:27:09 +01004651#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004652 int status;
4653 char one = nextopt("n");
4654#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004655 nextopt(nullstr);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004656#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004657 retval = 0;
4658
4659 argv = argptr;
Denys Vlasenko966f0872019-03-27 15:51:42 +01004660 if (!argv[0]) {
4661 /* wait for all jobs / one job if -n */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004662 for (;;) {
4663 jp = curjob;
Ron Yorstone48559e2019-03-31 09:27:09 +01004664#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004665 if (one && !jp)
4666 /* exitcode of "wait -n" with nothing to wait for is 127, not 0 */
4667 retval = 127;
4668#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004669 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004670 if (!jp) /* no running procs */
4671 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004672 if (jp->state == JOBRUNNING)
4673 break;
4674 jp->waited = 1;
4675 jp = jp->prev_job;
4676 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004677 /* man bash:
4678 * "When bash is waiting for an asynchronous command via
4679 * the wait builtin, the reception of a signal for which a trap
4680 * has been set will cause the wait builtin to return immediately
4681 * with an exit status greater than 128, immediately after which
4682 * the trap is executed."
Denys Vlasenko69188112016-10-27 20:18:18 +02004683 */
Ron Yorstone48559e2019-03-31 09:27:09 +01004684#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004685 status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL);
4686#else
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004687 dowait(DOWAIT_BLOCK_OR_SIG, NULL);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004688#endif
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004689 /* if child sends us a signal *and immediately exits*,
4690 * dowait() returns pid > 0. Check this case,
4691 * not "if (dowait() < 0)"!
4692 */
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004693 if (pending_sig)
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004694 goto sigout;
Ron Yorstone48559e2019-03-31 09:27:09 +01004695#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004696 if (one) {
4697 /* wait -n waits for one _job_, not one _process_.
4698 * date; sleep 3 & sleep 2 | sleep 1 & wait -n; date
4699 * should wait for 2 seconds. Not 1 or 3.
4700 */
4701 if (status != -1 && !WIFSTOPPED(status)) {
4702 retval = WEXITSTATUS(status);
4703 if (WIFSIGNALED(status))
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004704 retval = 128 | WTERMSIG(status);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004705 goto ret;
4706 }
4707 }
4708#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004709 }
4710 }
4711
4712 retval = 127;
4713 do {
4714 if (**argv != '%') {
4715 pid_t pid = number(*argv);
4716 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004717 while (1) {
4718 if (!job)
4719 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004720 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004721 break;
4722 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004723 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004724 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004725 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004726 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004727 /* loop until process terminated or stopped */
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004728 dowait(DOWAIT_BLOCK_OR_SIG, job);
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004729 if (pending_sig)
4730 goto sigout;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004731 job->waited = 1;
4732 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004733 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004734 } while (*++argv);
4735
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004736 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004737 return retval;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004738 sigout:
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004739 retval = 128 | pending_sig;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004740 return retval;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004741}
4742
4743static struct job *
4744growjobtab(void)
4745{
4746 size_t len;
4747 ptrdiff_t offset;
4748 struct job *jp, *jq;
4749
4750 len = njobs * sizeof(*jp);
4751 jq = jobtab;
4752 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4753
4754 offset = (char *)jp - (char *)jq;
4755 if (offset) {
4756 /* Relocate pointers */
4757 size_t l = len;
4758
4759 jq = (struct job *)((char *)jq + l);
4760 while (l) {
4761 l -= sizeof(*jp);
4762 jq--;
4763#define joff(p) ((struct job *)((char *)(p) + l))
4764#define jmove(p) (p) = (void *)((char *)(p) + offset)
4765 if (joff(jp)->ps == &jq->ps0)
4766 jmove(joff(jp)->ps);
4767 if (joff(jp)->prev_job)
4768 jmove(joff(jp)->prev_job);
4769 }
4770 if (curjob)
4771 jmove(curjob);
4772#undef joff
4773#undef jmove
4774 }
4775
4776 njobs += 4;
4777 jobtab = jp;
4778 jp = (struct job *)((char *)jp + len);
4779 jq = jp + 3;
4780 do {
4781 jq->used = 0;
4782 } while (--jq >= jp);
4783 return jp;
4784}
4785
4786/*
4787 * Return a new job structure.
4788 * Called with interrupts off.
4789 */
4790static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004791makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004792{
4793 int i;
4794 struct job *jp;
4795
4796 for (i = njobs, jp = jobtab; ; jp++) {
4797 if (--i < 0) {
4798 jp = growjobtab();
4799 break;
4800 }
4801 if (jp->used == 0)
4802 break;
4803 if (jp->state != JOBDONE || !jp->waited)
4804 continue;
4805#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004806 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004807 continue;
4808#endif
4809 freejob(jp);
4810 break;
4811 }
4812 memset(jp, 0, sizeof(*jp));
4813#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004814 /* jp->jobctl is a bitfield.
Denys Vlasenko098b7132017-01-11 19:59:03 +01004815 * "jp->jobctl |= doing_jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004816 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004817 jp->jobctl = 1;
4818#endif
4819 jp->prev_job = curjob;
4820 curjob = jp;
4821 jp->used = 1;
4822 jp->ps = &jp->ps0;
4823 if (nprocs > 1) {
4824 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4825 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004826 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004827 jobno(jp)));
4828 return jp;
4829}
4830
4831#if JOBS
4832/*
4833 * Return a string identifying a command (to be printed by the
4834 * jobs command).
4835 */
4836static char *cmdnextc;
4837
4838static void
4839cmdputs(const char *s)
4840{
Denys Vlasenko965b7952020-11-30 13:03:03 +01004841 static const char vstype[VSTYPE + 1][3] ALIGN1 = {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004842 "", "}", "-", "+", "?", "=",
4843 "%", "%%", "#", "##"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01004844 IF_BASH_SUBSTR(, ":")
4845 IF_BASH_PATTERN_SUBST(, "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004846 };
4847
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004848 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004849 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004850 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004851 unsigned char c;
4852 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004853 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004854
Denys Vlasenko46a14772009-12-10 21:27:13 +01004855 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004856 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4857 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004858 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004859 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004860 switch (c) {
4861 case CTLESC:
4862 c = *p++;
4863 break;
4864 case CTLVAR:
4865 subtype = *p++;
4866 if ((subtype & VSTYPE) == VSLENGTH)
4867 str = "${#";
4868 else
4869 str = "${";
Ron Yorston549deab2015-05-18 09:57:51 +02004870 goto dostr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004871 case CTLENDVAR:
Denys Vlasenko3f4847b2020-02-16 19:06:42 +01004872 str = "\"}";
4873 str += !(quoted & 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004874 quoted >>= 1;
4875 subtype = 0;
4876 goto dostr;
Ron Yorstona1b0d382020-07-23 08:32:27 +01004877#if BASH_PROCESS_SUBST
4878 case CTLBACKQ:
4879 c = '$';
4880 str = "(...)";
4881 break;
4882 case CTLTOPROC:
4883 c = '>';
4884 str = "(...)";
4885 break;
4886 case CTLFROMPROC:
4887 c = '<';
4888 str = "(...)";
4889 break;
4890#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004891 case CTLBACKQ:
4892 str = "$(...)";
4893 goto dostr;
Ron Yorstona1b0d382020-07-23 08:32:27 +01004894#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01004895#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004896 case CTLARI:
4897 str = "$((";
4898 goto dostr;
4899 case CTLENDARI:
4900 str = "))";
4901 goto dostr;
4902#endif
4903 case CTLQUOTEMARK:
4904 quoted ^= 1;
4905 c = '"';
4906 break;
4907 case '=':
4908 if (subtype == 0)
4909 break;
4910 if ((subtype & VSTYPE) != VSNORMAL)
4911 quoted <<= 1;
4912 str = vstype[subtype & VSTYPE];
4913 if (subtype & VSNUL)
4914 c = ':';
4915 else
4916 goto checkstr;
4917 break;
4918 case '\'':
4919 case '\\':
4920 case '"':
4921 case '$':
4922 /* These can only happen inside quotes */
4923 cc[0] = c;
4924 str = cc;
Denys Vlasenkod0fff912017-07-31 14:32:18 +02004925//FIXME:
4926// $ true $$ &
4927// $ <cr>
4928// [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004929 c = '\\';
4930 break;
4931 default:
4932 break;
4933 }
4934 USTPUTC(c, nextc);
4935 checkstr:
4936 if (!str)
4937 continue;
4938 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004939 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004940 USTPUTC(c, nextc);
4941 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004942 } /* while *p++ not NUL */
4943
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004944 if (quoted & 1) {
4945 USTPUTC('"', nextc);
4946 }
4947 *nextc = 0;
4948 cmdnextc = nextc;
4949}
4950
4951/* cmdtxt() and cmdlist() call each other */
4952static void cmdtxt(union node *n);
4953
4954static void
4955cmdlist(union node *np, int sep)
4956{
4957 for (; np; np = np->narg.next) {
4958 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004959 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004960 cmdtxt(np);
4961 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004962 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004963 }
4964}
4965
4966static void
4967cmdtxt(union node *n)
4968{
4969 union node *np;
4970 struct nodelist *lp;
4971 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004972
4973 if (!n)
4974 return;
4975 switch (n->type) {
4976 default:
4977#if DEBUG
4978 abort();
4979#endif
4980 case NPIPE:
4981 lp = n->npipe.cmdlist;
4982 for (;;) {
4983 cmdtxt(lp->n);
4984 lp = lp->next;
4985 if (!lp)
4986 break;
4987 cmdputs(" | ");
4988 }
4989 break;
4990 case NSEMI:
4991 p = "; ";
4992 goto binop;
4993 case NAND:
4994 p = " && ";
4995 goto binop;
4996 case NOR:
4997 p = " || ";
4998 binop:
4999 cmdtxt(n->nbinary.ch1);
5000 cmdputs(p);
5001 n = n->nbinary.ch2;
5002 goto donode;
5003 case NREDIR:
5004 case NBACKGND:
5005 n = n->nredir.n;
5006 goto donode;
5007 case NNOT:
5008 cmdputs("!");
5009 n = n->nnot.com;
5010 donode:
5011 cmdtxt(n);
5012 break;
5013 case NIF:
5014 cmdputs("if ");
5015 cmdtxt(n->nif.test);
5016 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005017 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02005018 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005019 cmdputs("; else ");
5020 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02005021 } else {
5022 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005023 }
5024 p = "; fi";
5025 goto dotail;
5026 case NSUBSHELL:
5027 cmdputs("(");
5028 n = n->nredir.n;
5029 p = ")";
5030 goto dotail;
5031 case NWHILE:
5032 p = "while ";
5033 goto until;
5034 case NUNTIL:
5035 p = "until ";
5036 until:
5037 cmdputs(p);
5038 cmdtxt(n->nbinary.ch1);
5039 n = n->nbinary.ch2;
5040 p = "; done";
5041 dodo:
5042 cmdputs("; do ");
5043 dotail:
5044 cmdtxt(n);
5045 goto dotail2;
5046 case NFOR:
5047 cmdputs("for ");
5048 cmdputs(n->nfor.var);
5049 cmdputs(" in ");
5050 cmdlist(n->nfor.args, 1);
5051 n = n->nfor.body;
5052 p = "; done";
5053 goto dodo;
5054 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01005055 cmdputs(n->ndefun.text);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005056 p = "() { ... }";
5057 goto dotail2;
5058 case NCMD:
5059 cmdlist(n->ncmd.args, 1);
5060 cmdlist(n->ncmd.redirect, 0);
5061 break;
5062 case NARG:
5063 p = n->narg.text;
5064 dotail2:
5065 cmdputs(p);
5066 break;
5067 case NHERE:
5068 case NXHERE:
5069 p = "<<...";
5070 goto dotail2;
5071 case NCASE:
5072 cmdputs("case ");
5073 cmdputs(n->ncase.expr->narg.text);
5074 cmdputs(" in ");
5075 for (np = n->ncase.cases; np; np = np->nclist.next) {
5076 cmdtxt(np->nclist.pattern);
5077 cmdputs(") ");
5078 cmdtxt(np->nclist.body);
5079 cmdputs(";; ");
5080 }
5081 p = "esac";
5082 goto dotail2;
5083 case NTO:
5084 p = ">";
5085 goto redir;
5086 case NCLOBBER:
5087 p = ">|";
5088 goto redir;
5089 case NAPPEND:
5090 p = ">>";
5091 goto redir;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005092#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005093 case NTO2:
5094#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005095 case NTOFD:
5096 p = ">&";
5097 goto redir;
5098 case NFROM:
5099 p = "<";
5100 goto redir;
5101 case NFROMFD:
5102 p = "<&";
5103 goto redir;
5104 case NFROMTO:
5105 p = "<>";
5106 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005107 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005108 cmdputs(p);
5109 if (n->type == NTOFD || n->type == NFROMFD) {
Denys Vlasenkod0fff912017-07-31 14:32:18 +02005110 if (n->ndup.dupfd >= 0)
5111 cmdputs(utoa(n->ndup.dupfd));
5112 else
5113 cmdputs("-");
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005114 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005115 }
5116 n = n->nfile.fname;
5117 goto donode;
5118 }
5119}
5120
5121static char *
5122commandtext(union node *n)
5123{
5124 char *name;
5125
5126 STARTSTACKSTR(cmdnextc);
5127 cmdtxt(n);
5128 name = stackblock();
Denys Vlasenko6a94cee2016-10-25 17:40:25 +02005129 TRACE(("commandtext: name %p, end %p\n", name, cmdnextc));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005130 return ckstrdup(name);
5131}
5132#endif /* JOBS */
5133
5134/*
5135 * Fork off a subshell. If we are doing job control, give the subshell its
5136 * own process group. Jp is a job structure that the job is to be added to.
5137 * N is the command that will be evaluated by the child. Both jp and n may
5138 * be NULL. The mode parameter can be one of the following:
5139 * FORK_FG - Fork off a foreground process.
5140 * FORK_BG - Fork off a background process.
5141 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
5142 * process group even if job control is on.
5143 *
5144 * When job control is turned off, background processes have their standard
5145 * input redirected to /dev/null (except for the second and later processes
5146 * in a pipeline).
5147 *
5148 * Called with interrupts off.
5149 */
5150/*
5151 * Clear traps on a fork.
5152 */
5153static void
5154clear_traps(void)
5155{
5156 char **tp;
5157
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005158 INT_OFF;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02005159 for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005160 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005161 if (trap_ptr == trap)
5162 free(*tp);
5163 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005164 *tp = NULL;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02005165 if ((tp - trap) != 0 && (tp - trap) < NSIG)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005166 setsignal(tp - trap);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005167 }
5168 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02005169 may_have_traps = 0;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005170 INT_ON;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005171}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005172
5173/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005174static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00005175
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005176/* Called after fork(), in child */
Denys Vlasenko70392332016-10-27 02:31:55 +02005177/* jp and n are NULL when called by openhere() for heredoc support */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02005178static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005179forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005180{
5181 int oldlvl;
5182
5183 TRACE(("Child shell %d\n", getpid()));
5184 oldlvl = shlvl;
5185 shlvl++;
5186
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005187 /* man bash: "Non-builtin commands run by bash have signal handlers
5188 * set to the values inherited by the shell from its parent".
5189 * Do we do it correctly? */
5190
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005191 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02005192
5193 if (mode == FORK_NOJOB /* is it `xxx` ? */
5194 && n && n->type == NCMD /* is it single cmd? */
5195 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01005196 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005197 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
5198 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
5199 ) {
5200 TRACE(("Trap hack\n"));
5201 /* Awful hack for `trap` or $(trap).
5202 *
5203 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5204 * contains an example where "trap" is executed in a subshell:
5205 *
5206 * save_traps=$(trap)
5207 * ...
5208 * eval "$save_traps"
5209 *
5210 * Standard does not say that "trap" in subshell shall print
5211 * parent shell's traps. It only says that its output
5212 * must have suitable form, but then, in the above example
5213 * (which is not supposed to be normative), it implies that.
5214 *
5215 * bash (and probably other shell) does implement it
5216 * (traps are reset to defaults, but "trap" still shows them),
5217 * but as a result, "trap" logic is hopelessly messed up:
5218 *
5219 * # trap
5220 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5221 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5222 * # true | trap <--- trap is in subshell - no output (ditto)
5223 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5224 * trap -- 'echo Ho' SIGWINCH
5225 * # echo `(trap)` <--- in subshell in subshell - output
5226 * trap -- 'echo Ho' SIGWINCH
5227 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5228 * trap -- 'echo Ho' SIGWINCH
5229 *
5230 * The rules when to forget and when to not forget traps
5231 * get really complex and nonsensical.
5232 *
5233 * Our solution: ONLY bare $(trap) or `trap` is special.
5234 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005235 /* Save trap handler strings for trap builtin to print */
Ron Yorstond840c5d2015-07-19 23:05:20 +02005236 trap_ptr = xmemdup(trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005237 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02005238 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005239 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005240#if JOBS
5241 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005242 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005243 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005244 pid_t pgrp;
5245
5246 if (jp->nprocs == 0)
5247 pgrp = getpid();
5248 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005249 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005250 /* this can fail because we are doing it in the parent also */
5251 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005252 if (mode == FORK_FG)
5253 xtcsetpgrp(ttyfd, pgrp);
5254 setsignal(SIGTSTP);
5255 setsignal(SIGTTOU);
5256 } else
5257#endif
5258 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005259 /* man bash: "When job control is not in effect,
5260 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005261 ignoresig(SIGINT);
5262 ignoresig(SIGQUIT);
5263 if (jp->nprocs == 0) {
5264 close(0);
5265 if (open(bb_dev_null, O_RDONLY) != 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005266 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005267 }
5268 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005269 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005270 if (iflag) { /* why if iflag only? */
5271 setsignal(SIGINT);
5272 setsignal(SIGTERM);
5273 }
5274 /* man bash:
5275 * "In all cases, bash ignores SIGQUIT. Non-builtin
5276 * commands run by bash have signal handlers
5277 * set to the values inherited by the shell
5278 * from its parent".
5279 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005280 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005281 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005282#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02005283 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01005284 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005285 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005286 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02005287 /* "jobs": we do not want to clear job list for it,
5288 * instead we remove only _its_ own_ job from job list.
5289 * This makes "jobs .... | cat" more useful.
5290 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005291 freejob(curjob);
5292 return;
5293 }
5294#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005295 for (jp = curjob; jp; jp = jp->prev_job)
5296 freejob(jp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005297}
5298
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005299/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00005300#if !JOBS
5301#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5302#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005303static void
5304forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5305{
5306 TRACE(("In parent shell: child = %d\n", pid));
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005307 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005308 return;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005309#if JOBS
5310 if (mode != FORK_NOJOB && jp->jobctl) {
5311 int pgrp;
5312
5313 if (jp->nprocs == 0)
5314 pgrp = pid;
5315 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005316 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005317 /* This can fail because we are doing it in the child also */
5318 setpgid(pid, pgrp);
5319 }
5320#endif
5321 if (mode == FORK_BG) {
5322 backgndpid = pid; /* set $! */
5323 set_curjob(jp, CUR_RUNNING);
5324 }
5325 if (jp) {
5326 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01005327 ps->ps_pid = pid;
5328 ps->ps_status = -1;
5329 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005330#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005331 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01005332 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005333#endif
5334 }
5335}
5336
Denys Vlasenko70392332016-10-27 02:31:55 +02005337/* jp and n are NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005338static int
5339forkshell(struct job *jp, union node *n, int mode)
5340{
5341 int pid;
5342
5343 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
5344 pid = fork();
5345 if (pid < 0) {
5346 TRACE(("Fork failed, errno=%d", errno));
5347 if (jp)
5348 freejob(jp);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005349 ash_msg_and_raise_perror("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005350 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02005351 if (pid == 0) {
5352 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005353 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005354 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005355 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005356 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005357 return pid;
5358}
5359
5360/*
5361 * Wait for job to finish.
5362 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005363 * Under job control we have the problem that while a child process
5364 * is running interrupts generated by the user are sent to the child
5365 * but not to the shell. This means that an infinite loop started by
5366 * an interactive user may be hard to kill. With job control turned off,
5367 * an interactive user may place an interactive program inside a loop.
5368 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005369 * these interrupts to also abort the loop. The approach we take here
5370 * is to have the shell ignore interrupt signals while waiting for a
5371 * foreground process to terminate, and then send itself an interrupt
5372 * signal if the child process was terminated by an interrupt signal.
5373 * Unfortunately, some programs want to do a bit of cleanup and then
5374 * exit on interrupt; unless these processes terminate themselves by
5375 * sending a signal to themselves (instead of calling exit) they will
5376 * confuse this approach.
5377 *
5378 * Called with interrupts off.
5379 */
5380static int
5381waitforjob(struct job *jp)
5382{
5383 int st;
5384
Denys Vlasenkod81af722020-02-18 14:28:30 +01005385 TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005386
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005387 /* In non-interactive shells, we _can_ get
5388 * a keyboard signal here and be EINTRed, but we just loop
5389 * inside dowait(), waiting for command to complete.
5390 *
5391 * man bash:
5392 * "If bash is waiting for a command to complete and receives
5393 * a signal for which a trap has been set, the trap
5394 * will not be executed until the command completes."
5395 *
5396 * Reality is that even if trap is not set, bash
5397 * will not act on the signal until command completes.
5398 * Try this. sleep5intoff.c:
5399 * #include <signal.h>
5400 * #include <unistd.h>
5401 * int main() {
5402 * sigset_t set;
5403 * sigemptyset(&set);
5404 * sigaddset(&set, SIGINT);
5405 * sigaddset(&set, SIGQUIT);
5406 * sigprocmask(SIG_BLOCK, &set, NULL);
5407 * sleep(5);
5408 * return 0;
5409 * }
5410 * $ bash -c './sleep5intoff; echo hi'
5411 * ^C^C^C^C <--- pressing ^C once a second
5412 * $ _
5413 * $ bash -c './sleep5intoff; echo hi'
5414 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
5415 * $ _
5416 */
5417 dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
5418 if (!jp)
Denys Vlasenko97edfc42020-02-18 14:37:56 +01005419 return exitstatus;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005420
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005421 st = getstatus(jp);
5422#if JOBS
5423 if (jp->jobctl) {
5424 xtcsetpgrp(ttyfd, rootpid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01005425 restore_tty_if_stopped_or_signaled(jp);
5426
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005427 /*
5428 * This is truly gross.
5429 * If we're doing job control, then we did a TIOCSPGRP which
5430 * caused us (the shell) to no longer be in the controlling
5431 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
5432 * intuit from the subprocess exit status whether a SIGINT
5433 * occurred, and if so interrupt ourselves. Yuck. - mycroft
5434 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005435 if (jp->sigint) /* TODO: do the same with all signals */
5436 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005437 }
5438 if (jp->state == JOBDONE)
5439#endif
5440 freejob(jp);
5441 return st;
5442}
5443
5444/*
5445 * return 1 if there are stopped jobs, otherwise 0
5446 */
5447static int
5448stoppedjobs(void)
5449{
5450 struct job *jp;
5451 int retval;
5452
5453 retval = 0;
Ron Yorston50239a62021-09-12 11:21:08 +01005454 if (!iflag || job_warning)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005455 goto out;
5456 jp = curjob;
5457 if (jp && jp->state == JOBSTOPPED) {
5458 out2str("You have stopped jobs.\n");
5459 job_warning = 2;
5460 retval++;
5461 }
5462 out:
5463 return retval;
5464}
5465
5466
Denys Vlasenko70392332016-10-27 02:31:55 +02005467/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005468 * Code for dealing with input/output redirection.
5469 */
5470
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01005471#undef EMPTY
5472#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005473#define EMPTY -2 /* marks an unused slot in redirtab */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005474#define CLOSED -1 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005475
5476/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005477 * Handle here documents. Normally we fork off a process to write the
5478 * data to a pipe. If the document is short, we can stuff the data in
5479 * the pipe without forking.
5480 */
5481/* openhere needs this forward reference */
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005482static void expandhere(union node *arg);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005483static int
5484openhere(union node *redir)
5485{
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005486 char *p;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005487 int pip[2];
5488 size_t len = 0;
5489
5490 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005491 ash_msg_and_raise_perror("can't create pipe");
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005492
5493 p = redir->nhere.doc->narg.text;
5494 if (redir->type == NXHERE) {
5495 expandhere(redir->nhere.doc);
5496 p = stackblock();
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005497 }
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005498
5499 len = strlen(p);
5500 if (len <= PIPE_BUF) {
5501 xwrite(pip[1], p, len);
5502 goto out;
5503 }
5504
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005505 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005506 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005507 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005508 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5509 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5510 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5511 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005512 signal(SIGPIPE, SIG_DFL);
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005513 xwrite(pip[1], p, len);
Denys Vlasenkodb5546c2022-01-05 22:16:06 +01005514 _exit_SUCCESS();
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005515 }
5516 out:
5517 close(pip[1]);
5518 return pip[0];
5519}
5520
5521static int
5522openredirect(union node *redir)
5523{
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005524 struct stat sb;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005525 char *fname;
5526 int f;
5527
5528 switch (redir->nfile.type) {
Denys Vlasenko557482c2016-09-25 21:24:04 +02005529/* Can't happen, our single caller does this itself */
5530// case NTOFD:
5531// case NFROMFD:
5532// return -1;
5533 case NHERE:
5534 case NXHERE:
5535 return openhere(redir);
5536 }
5537
5538 /* For N[X]HERE, reading redir->nfile.expfname would touch beyond
5539 * allocated space. Do it only when we know it is safe.
5540 */
5541 fname = redir->nfile.expfname;
5542
5543 switch (redir->nfile.type) {
5544 default:
5545#if DEBUG
5546 abort();
5547#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005548 case NFROM:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005549 f = open(fname, O_RDONLY);
5550 if (f < 0)
5551 goto eopen;
5552 break;
5553 case NFROMTO:
Andreas Bühmannda75f442010-06-24 04:32:37 +02005554 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005555 if (f < 0)
5556 goto ecreate;
5557 break;
5558 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005559#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005560 case NTO2:
5561#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005562 /* Take care of noclobber mode. */
5563 if (Cflag) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005564 if (stat(fname, &sb) < 0) {
5565 f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5566 if (f < 0)
5567 goto ecreate;
5568 } else if (!S_ISREG(sb.st_mode)) {
5569 f = open(fname, O_WRONLY, 0666);
5570 if (f < 0)
5571 goto ecreate;
Denys Vlasenko355ec352018-04-02 13:34:57 +02005572 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005573 close(f);
5574 errno = EEXIST;
5575 goto ecreate;
5576 }
5577 } else {
5578 errno = EEXIST;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005579 goto ecreate;
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005580 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005581 break;
5582 }
5583 /* FALLTHROUGH */
5584 case NCLOBBER:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005585 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5586 if (f < 0)
5587 goto ecreate;
5588 break;
5589 case NAPPEND:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005590 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5591 if (f < 0)
5592 goto ecreate;
5593 break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005594 }
5595
5596 return f;
5597 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005598 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005599 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005600 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005601}
5602
5603/*
Denys Vlasenko64774602016-10-26 15:24:30 +02005604 * Copy a file descriptor to be >= 10. Throws exception on error.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005605 */
5606static int
Denys Vlasenko64774602016-10-26 15:24:30 +02005607savefd(int from)
5608{
5609 int newfd;
5610 int err;
5611
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005612 newfd = fcntl(from, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko64774602016-10-26 15:24:30 +02005613 err = newfd < 0 ? errno : 0;
5614 if (err != EBADF) {
5615 if (err)
Ron Yorstonbe366e52017-07-27 13:53:39 +01005616 ash_msg_and_raise_perror("%d", from);
Denys Vlasenko64774602016-10-26 15:24:30 +02005617 close(from);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005618 if (F_DUPFD_CLOEXEC == F_DUPFD)
5619 close_on_exec_on(newfd);
Denys Vlasenko64774602016-10-26 15:24:30 +02005620 }
5621
5622 return newfd;
5623}
5624static int
5625dup2_or_raise(int from, int to)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005626{
5627 int newfd;
5628
Denys Vlasenko64774602016-10-26 15:24:30 +02005629 newfd = (from != to) ? dup2(from, to) : to;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005630 if (newfd < 0) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005631 /* Happens when source fd is not open: try "echo >&99" */
Ron Yorstonbe366e52017-07-27 13:53:39 +01005632 ash_msg_and_raise_perror("%d", from);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005633 }
5634 return newfd;
5635}
Denys Vlasenko035486c2017-07-31 04:09:19 +02005636static int
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005637dup_CLOEXEC(int fd, int avoid_fd)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005638{
5639 int newfd;
5640 repeat:
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005641 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
5642 if (newfd >= 0) {
5643 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005644 close_on_exec_on(newfd);
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005645 } else { /* newfd < 0 */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005646 if (errno == EBUSY)
5647 goto repeat;
5648 if (errno == EINTR)
5649 goto repeat;
5650 }
5651 return newfd;
5652}
5653static int
5654xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5655{
5656 int newfd;
5657 repeat:
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005658 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005659 if (newfd < 0) {
5660 if (errno == EBUSY)
5661 goto repeat;
5662 if (errno == EINTR)
5663 goto repeat;
5664 /* fd was not open? */
5665 if (errno == EBADF)
5666 return fd;
5667 ash_msg_and_raise_perror("%d", newfd);
5668 }
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005669 if (F_DUPFD_CLOEXEC == F_DUPFD)
5670 close_on_exec_on(newfd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005671 close(fd);
5672 return newfd;
5673}
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005674
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005675/* Struct def and variable are moved down to the first usage site */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005676struct squirrel {
5677 int orig_fd;
5678 int moved_to;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005679};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005680struct redirtab {
5681 struct redirtab *next;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005682 int pair_count;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005683 struct squirrel two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005684};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005685#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005686
Denys Vlasenko035486c2017-07-31 04:09:19 +02005687static void
5688add_squirrel_closed(struct redirtab *sq, int fd)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005689{
5690 int i;
5691
Denys Vlasenko035486c2017-07-31 04:09:19 +02005692 if (!sq)
5693 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005694
Denys Vlasenko035486c2017-07-31 04:09:19 +02005695 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5696 /* If we collide with an already moved fd... */
5697 if (fd == sq->two_fd[i].orig_fd) {
5698 /* Examples:
5699 * "echo 3>FILE 3>&- 3>FILE"
5700 * "echo 3>&- 3>FILE"
5701 * No need for last redirect to insert
5702 * another "need to close 3" indicator.
5703 */
5704 TRACE(("redirect_fd %d: already moved or closed\n", fd));
5705 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005706 }
5707 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005708 TRACE(("redirect_fd %d: previous fd was closed\n", fd));
5709 sq->two_fd[i].orig_fd = fd;
5710 sq->two_fd[i].moved_to = CLOSED;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005711}
5712
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02005713static int
Denys Vlasenko035486c2017-07-31 04:09:19 +02005714save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005715{
Denys Vlasenko035486c2017-07-31 04:09:19 +02005716 int i, new_fd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005717
Denys Vlasenko035486c2017-07-31 04:09:19 +02005718 if (avoid_fd < 9) /* the important case here is that it can be -1 */
5719 avoid_fd = 9;
5720
5721#if JOBS
5722 if (fd == ttyfd) {
5723 /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */
5724 ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd);
5725 TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd));
5726 return 1; /* "we closed fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005727 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005728#endif
5729 /* Are we called from redirect(0)? E.g. redirect
5730 * in a forked child. No need to save fds,
5731 * we aren't going to use them anymore, ok to trash.
5732 */
5733 if (!sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005734 return 0;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005735
5736 /* If this one of script's fds? */
5737 if (fd != 0) {
5738 struct parsefile *pf = g_parsefile;
5739 while (pf) {
5740 /* We skip fd == 0 case because of the following:
5741 * $ ash # running ash interactively
5742 * $ . ./script.sh
5743 * and in script.sh: "exec 9>&0".
5744 * Even though top-level pf_fd _is_ 0,
5745 * it's still ok to use it: "read" builtin uses it,
5746 * why should we cripple "exec" builtin?
5747 */
5748 if (fd == pf->pf_fd) {
5749 pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
5750 return 1; /* "we closed fd" */
5751 }
5752 pf = pf->prev;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005753 }
5754 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005755
5756 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
5757
5758 /* First: do we collide with some already moved fds? */
5759 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5760 /* If we collide with an already moved fd... */
5761 if (fd == sq->two_fd[i].moved_to) {
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005762 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005763 sq->two_fd[i].moved_to = new_fd;
5764 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
5765 if (new_fd < 0) /* what? */
5766 xfunc_die();
5767 return 0; /* "we did not close fd" */
5768 }
5769 if (fd == sq->two_fd[i].orig_fd) {
5770 /* Example: echo Hello >/dev/null 1>&2 */
5771 TRACE(("redirect_fd %d: already moved\n", fd));
5772 return 0; /* "we did not close fd" */
5773 }
5774 }
5775
5776 /* 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 +02005777 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005778 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
5779 if (new_fd < 0) {
5780 if (errno != EBADF)
5781 xfunc_die();
5782 /* new_fd = CLOSED; - already is -1 */
5783 }
5784 sq->two_fd[i].moved_to = new_fd;
5785 sq->two_fd[i].orig_fd = fd;
5786
5787 /* if we move stderr, let "set -x" code know */
5788 if (fd == preverrout_fd)
5789 preverrout_fd = new_fd;
5790
5791 return 0; /* "we did not close fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005792}
5793
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005794static int
5795internally_opened_fd(int fd, struct redirtab *sq)
5796{
5797 int i;
5798#if JOBS
5799 if (fd == ttyfd)
5800 return 1;
5801#endif
5802 /* If this one of script's fds? */
5803 if (fd != 0) {
5804 struct parsefile *pf = g_parsefile;
5805 while (pf) {
5806 if (fd == pf->pf_fd)
5807 return 1;
5808 pf = pf->prev;
5809 }
5810 }
5811
5812 if (sq) for (i = 0; i < sq->pair_count && sq->two_fd[i].orig_fd != EMPTY; i++) {
5813 if (fd == sq->two_fd[i].moved_to)
5814 return 1;
5815 }
5816 return 0;
5817}
5818
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005819/*
5820 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5821 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005822 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005823 */
5824/* flags passed to redirect */
5825#define REDIR_PUSH 01 /* save previous values of file descriptors */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005826static void
5827redirect(union node *redir, int flags)
5828{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005829 struct redirtab *sv;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005830
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005831 if (!redir)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005832 return;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005833
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005834 sv = NULL;
5835 INT_OFF;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005836 if (flags & REDIR_PUSH)
5837 sv = redirlist;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005838 do {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005839 int fd;
5840 int newfd;
5841 int close_fd;
5842 int closed;
5843
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005844 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005845 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005846 //bb_error_msg("doing %d > %d", fd, newfd);
5847 newfd = redir->ndup.dupfd;
5848 close_fd = -1;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005849 } else {
5850 newfd = openredirect(redir); /* always >= 0 */
5851 if (fd == newfd) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005852 /* open() gave us precisely the fd we wanted.
5853 * This means that this fd was not busy
5854 * (not opened to anywhere).
5855 * Remember to close it on restore:
5856 */
5857 add_squirrel_closed(sv, fd);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005858 continue;
5859 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005860 close_fd = newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005861 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005862
5863 if (fd == newfd)
5864 continue;
5865
5866 /* if "N>FILE": move newfd to fd */
5867 /* if "N>&M": dup newfd to fd */
5868 /* if "N>&-": close fd (newfd is -1) */
5869
5870 IF_BASH_REDIR_OUTPUT(redirect_more:)
5871
5872 closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv);
5873 if (newfd == -1) {
5874 /* "N>&-" means "close me" */
5875 if (!closed) {
5876 /* ^^^ optimization: saving may already
5877 * have closed it. If not... */
5878 close(fd);
Denis Vlasenko22f74142008-07-24 22:34:43 +00005879 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005880 } else {
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005881 /* if newfd is a script fd or saved fd, simulate EBADF */
5882 if (internally_opened_fd(newfd, sv)) {
5883 errno = EBADF;
5884 ash_msg_and_raise_perror("%d", newfd);
5885 }
Denys Vlasenko64774602016-10-26 15:24:30 +02005886 dup2_or_raise(newfd, fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005887 if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
5888 close(close_fd);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005889#if BASH_REDIR_OUTPUT
Denys Vlasenko035486c2017-07-31 04:09:19 +02005890 if (redir->nfile.type == NTO2 && fd == 1) {
5891 /* ">&FILE". we already redirected to 1, now copy 1 to 2 */
5892 fd = 2;
5893 newfd = 1;
5894 close_fd = -1;
5895 goto redirect_more;
5896 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005897#endif
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005898 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005899 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005900 INT_ON;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005901
5902//dash:#define REDIR_SAVEFD2 03 /* set preverrout */
5903#define REDIR_SAVEFD2 0
5904 // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
5905 // triggers for pure REDIR_PUSH too. Thus, this is done almost always,
5906 // not only for calls with flags containing REDIR_SAVEFD2.
Denys Vlasenko035486c2017-07-31 04:09:19 +02005907 // We do this unconditionally (see save_fd_on_redirect()).
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005908 //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5909 // preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005910}
5911
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005912static int
5913redirectsafe(union node *redir, int flags)
5914{
5915 int err;
5916 volatile int saveint;
5917 struct jmploc *volatile savehandler = exception_handler;
5918 struct jmploc jmploc;
5919
5920 SAVE_INT(saveint);
5921 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005922 err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005923 if (!err) {
5924 exception_handler = &jmploc;
5925 redirect(redir, flags);
5926 }
5927 exception_handler = savehandler;
5928 if (err && exception_type != EXERROR)
5929 longjmp(exception_handler->loc, 1);
5930 RESTORE_INT(saveint);
5931 return err;
5932}
5933
Ron Yorstona1b0d382020-07-23 08:32:27 +01005934#if BASH_PROCESS_SUBST
5935static void
5936pushfd(int fd)
5937{
5938 struct redirtab *sv;
5939
5940 sv = ckzalloc(sizeof(*sv) + sizeof(sv->two_fd[0]));
5941 sv->pair_count = 1;
5942 sv->two_fd[0].orig_fd = fd;
5943 sv->two_fd[0].moved_to = CLOSED;
5944 sv->next = redirlist;
5945 redirlist = sv;
5946}
5947#endif
5948
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005949static struct redirtab*
5950pushredir(union node *redir)
5951{
5952 struct redirtab *sv;
5953 int i;
5954
5955 if (!redir)
5956 return redirlist;
5957
5958 i = 0;
5959 do {
5960 i++;
5961#if BASH_REDIR_OUTPUT
5962 if (redir->nfile.type == NTO2)
5963 i++;
5964#endif
5965 redir = redir->nfile.next;
5966 } while (redir);
5967
5968 sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
5969 sv->pair_count = i;
5970 while (--i >= 0)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005971 sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005972 sv->next = redirlist;
5973 redirlist = sv;
5974 return sv->next;
5975}
5976
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005977/*
5978 * Undo the effects of the last redirection.
5979 */
5980static void
Denys Vlasenko035486c2017-07-31 04:09:19 +02005981popredir(int drop)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005982{
5983 struct redirtab *rp;
5984 int i;
5985
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02005986 if (redirlist == NULL)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005987 return;
5988 INT_OFF;
5989 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005990 for (i = 0; i < rp->pair_count; i++) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005991 int fd = rp->two_fd[i].orig_fd;
5992 int copy = rp->two_fd[i].moved_to;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005993 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005994 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005995 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005996 continue;
5997 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005998 if (copy != EMPTY) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005999 if (!drop) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00006000 /*close(fd);*/
Denys Vlasenko64774602016-10-26 15:24:30 +02006001 dup2_or_raise(copy, fd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006002 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02006003 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006004 }
6005 }
6006 redirlist = rp->next;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006007 free(rp);
6008 INT_ON;
6009}
6010
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02006011static void
6012unwindredir(struct redirtab *stop)
6013{
6014 while (redirlist != stop)
Denys Vlasenko035486c2017-07-31 04:09:19 +02006015 popredir(/*drop:*/ 0);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02006016}
6017
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006018
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006019/* ============ Routines to expand arguments to commands
6020 *
6021 * We have to deal with backquotes, shell variables, and file metacharacters.
6022 */
6023
Denys Vlasenko0b883582016-12-23 16:49:07 +01006024#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006025static arith_t
6026ash_arith(const char *s)
6027{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006028 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006029 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006030
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006031 math_state.lookupvar = lookupvar;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02006032 math_state.setvar = setvar0;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006033 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006034
6035 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006036 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02006037 if (math_state.errmsg)
6038 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006039 INT_ON;
6040
6041 return result;
6042}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006043#endif
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01006044#if BASH_SUBSTR
6045# if ENABLE_FEATURE_SH_MATH
6046static int substr_atoi(const char *s)
6047{
6048 arith_t t = ash_arith(s);
6049 if (sizeof(t) > sizeof(int)) {
6050 /* clamp very large or very large negative nums for ${v:N:M}:
6051 * else "${v:0:0x100000001}" would work as "${v:0:1}"
6052 */
6053 if (t > INT_MAX)
6054 t = INT_MAX;
6055 if (t < INT_MIN)
6056 t = INT_MIN;
6057 }
6058 return t;
6059}
6060# else
6061# define substr_atoi(s) number(s)
6062# endif
6063#endif
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006064
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006065/*
6066 * expandarg flags
6067 */
6068#define EXP_FULL 0x1 /* perform word splitting & file globbing */
6069#define EXP_TILDE 0x2 /* do normal tilde expansion */
6070#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
6071#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
Denys Vlasenkodb74c6c2016-10-24 21:12:33 +02006072/* ^^^^^^^^^^^^^^ this is meant to support constructs such as "cmd >file*.txt"
6073 * POSIX says for this case:
6074 * Pathname expansion shall not be performed on the word by a
6075 * non-interactive shell; an interactive shell may perform it, but shall
6076 * do so only when the expansion would result in one word.
6077 * Currently, our code complies to the above rule by never globbing
6078 * redirection filenames.
6079 * Bash performs globbing, unless it is non-interactive and in POSIX mode.
6080 * (this means that on a typical Linux distro, bash almost always
6081 * performs globbing, and thus diverges from what we do).
6082 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006083#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006084#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6085#define EXP_WORD 0x40 /* expand word in parameter expansion */
Denys Vlasenko440da972018-08-05 14:29:58 +02006086#define EXP_QUOTED 0x100 /* expand word in double quotes */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006087#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
Denys Vlasenko82331882020-02-24 10:02:50 +01006088#define EXP_DISCARD 0x400 /* discard result of expansion */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006089
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006091 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006092 */
6093#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
6094#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006095#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
6096#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
6097
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006098/* Add CTLESC when necessary. */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006099#define QUOTES_ESC (EXP_FULL | EXP_CASE)
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006100
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006101/*
6102 * Structure specifying which parts of the string should be searched
6103 * for IFS characters.
6104 */
6105struct ifsregion {
6106 struct ifsregion *next; /* next region in list */
6107 int begoff; /* offset of start of region */
6108 int endoff; /* offset of end of region */
6109 int nulonly; /* search for nul bytes only */
6110};
6111
6112struct arglist {
6113 struct strlist *list;
6114 struct strlist **lastp;
6115};
6116
6117/* output of current string */
6118static char *expdest;
6119/* list of back quote expressions */
6120static struct nodelist *argbackq;
6121/* first struct in list of ifs regions */
6122static struct ifsregion ifsfirst;
6123/* last struct in list */
6124static struct ifsregion *ifslastp;
6125/* holds expanded arg list */
6126static struct arglist exparg;
6127
6128/*
Denys Vlasenko455e4222016-10-27 14:45:13 +02006129 * Break the argument string into pieces based upon IFS and add the
6130 * strings to the argument list. The regions of the string to be
6131 * searched for IFS characters have been stored by recordregion.
6132 */
6133static void
6134ifsbreakup(char *string, struct arglist *arglist)
6135{
6136 struct ifsregion *ifsp;
6137 struct strlist *sp;
6138 char *start;
6139 char *p;
6140 char *q;
6141 const char *ifs, *realifs;
6142 int ifsspc;
6143 int nulonly;
6144
6145 start = string;
6146 if (ifslastp != NULL) {
6147 ifsspc = 0;
6148 nulonly = 0;
6149 realifs = ifsset() ? ifsval() : defifs;
6150 ifsp = &ifsfirst;
6151 do {
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006152 int afternul;
6153
Denys Vlasenko455e4222016-10-27 14:45:13 +02006154 p = string + ifsp->begoff;
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006155 afternul = nulonly;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006156 nulonly = ifsp->nulonly;
6157 ifs = nulonly ? nullstr : realifs;
6158 ifsspc = 0;
6159 while (p < string + ifsp->endoff) {
6160 q = p;
6161 if ((unsigned char)*p == CTLESC)
6162 p++;
6163 if (!strchr(ifs, *p)) {
6164 p++;
6165 continue;
6166 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006167 if (!(afternul || nulonly))
Denys Vlasenko455e4222016-10-27 14:45:13 +02006168 ifsspc = (strchr(defifs, *p) != NULL);
6169 /* Ignore IFS whitespace at start */
6170 if (q == start && ifsspc) {
6171 p++;
6172 start = p;
6173 continue;
6174 }
6175 *q = '\0';
6176 sp = stzalloc(sizeof(*sp));
6177 sp->text = start;
6178 *arglist->lastp = sp;
6179 arglist->lastp = &sp->next;
6180 p++;
6181 if (!nulonly) {
6182 for (;;) {
6183 if (p >= string + ifsp->endoff) {
6184 break;
6185 }
6186 q = p;
6187 if ((unsigned char)*p == CTLESC)
6188 p++;
6189 if (strchr(ifs, *p) == NULL) {
6190 p = q;
6191 break;
6192 }
6193 if (strchr(defifs, *p) == NULL) {
6194 if (ifsspc) {
6195 p++;
6196 ifsspc = 0;
6197 } else {
6198 p = q;
6199 break;
6200 }
6201 } else
6202 p++;
6203 }
6204 }
6205 start = p;
6206 } /* while */
6207 ifsp = ifsp->next;
6208 } while (ifsp != NULL);
6209 if (nulonly)
6210 goto add;
6211 }
6212
6213 if (!*start)
6214 return;
6215
6216 add:
6217 sp = stzalloc(sizeof(*sp));
6218 sp->text = start;
6219 *arglist->lastp = sp;
6220 arglist->lastp = &sp->next;
6221}
6222
6223static void
6224ifsfree(void)
6225{
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006226 struct ifsregion *p = ifsfirst.next;
6227
6228 if (!p)
6229 goto out;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006230
6231 INT_OFF;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006232 do {
6233 struct ifsregion *ifsp;
6234 ifsp = p->next;
6235 free(p);
6236 p = ifsp;
6237 } while (p);
Denys Vlasenko455e4222016-10-27 14:45:13 +02006238 ifsfirst.next = NULL;
6239 INT_ON;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006240 out:
6241 ifslastp = NULL;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006242}
6243
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006244static size_t
6245esclen(const char *start, const char *p)
6246{
6247 size_t esc = 0;
6248
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006249 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006250 esc++;
6251 }
6252 return esc;
6253}
6254
6255/*
6256 * Remove any CTLESC characters from a string.
6257 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01006258#if !BASH_PATTERN_SUBST
6259#define rmescapes(str, flag, slash_position) \
6260 rmescapes(str, flag)
6261#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006262static char *
Denys Vlasenko740058b2018-01-09 17:01:00 +01006263rmescapes(char *str, int flag, int *slash_position)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006264{
Ron Yorston417622c2015-05-18 09:59:14 +02006265 static const char qchars[] ALIGN1 = {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006266 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00006267
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006268 char *p, *q, *r;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006269 unsigned protect_against_glob;
6270 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006271
Denys Vlasenko740058b2018-01-09 17:01:00 +01006272 p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash_position));
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006273 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006274 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006275
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006276 q = p;
6277 r = str;
6278 if (flag & RMESCAPE_ALLOC) {
6279 size_t len = p - str;
6280 size_t fulllen = len + strlen(p) + 1;
6281
6282 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02006283 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006284 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02006285 /* p and str may be invalidated by makestrspace */
6286 str = (char *)stackblock() + strloc;
6287 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006288 } else if (flag & RMESCAPE_HEAP) {
6289 r = ckmalloc(fulllen);
6290 } else {
6291 r = stalloc(fulllen);
6292 }
6293 q = r;
6294 if (len > 0) {
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02006295 q = (char *)mempcpy(q, str, len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296 }
6297 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006298
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006299 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006300 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006301 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006302 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenko216913c2018-04-02 12:35:04 +02006303// Note: protect_against_glob only affect whether
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006304// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006305 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006306 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006307 continue;
6308 }
Denys Vlasenko216913c2018-04-02 12:35:04 +02006309 if (*p == '\\') {
6310 /* naked back slash */
6311 protect_against_glob = 0;
6312 goto copy;
6313 }
Ron Yorston549deab2015-05-18 09:57:51 +02006314 if ((unsigned char)*p == CTLESC) {
6315 p++;
Denys Vlasenko13f20912016-09-25 20:54:25 +02006316#if DEBUG
6317 if (*p == '\0')
6318 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)");
6319#endif
Ron Yorston549deab2015-05-18 09:57:51 +02006320 if (protect_against_glob) {
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006321 /*
6322 * We used to trust glob() and fnmatch() to eat
6323 * superfluous escapes (\z where z has no
6324 * special meaning anyway). But this causes
6325 * bugs such as string of one greek letter rho
Denys Vlasenkoed79a632017-07-05 19:20:43 +02006326 * (unicode-encoded as two bytes "cf,81")
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006327 * getting encoded as "cf,CTLESC,81"
6328 * and here, converted to "cf,\,81" -
6329 * which does not go well with some flavors
Denys Vlasenko92b8d9c2017-07-05 19:13:44 +02006330 * of fnmatch() in unicode locales
6331 * (for example, glibc <= 2.22).
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006332 *
6333 * Lets add "\" only on the chars which need it.
Denys Vlasenko4142f012017-07-05 22:19:28 +02006334 * Testcases for less obvious chars are shown.
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006335 */
6336 if (*p == '*'
6337 || *p == '?'
6338 || *p == '['
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006339 || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */
6340 || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
6341 || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
6342 || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenko4142f012017-07-05 22:19:28 +02006343 /* Some libc support [^negate], that's why "^" also needs love */
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006344 || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006345 ) {
6346 *q++ = '\\';
6347 }
Ron Yorston549deab2015-05-18 09:57:51 +02006348 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006349 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006350#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01006351 else if (slash_position && p == str + *slash_position) {
6352 /* stop handling globbing */
6353 globbing = 0;
6354 *slash_position = q - r;
6355 slash_position = NULL;
Ron Yorston417622c2015-05-18 09:59:14 +02006356 }
6357#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006358 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006359 copy:
6360 *q++ = *p++;
6361 }
6362 *q = '\0';
6363 if (flag & RMESCAPE_GROW) {
6364 expdest = r;
6365 STADJUST(q - r + 1, expdest);
6366 }
6367 return r;
6368}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006369#define pmatch(a, b) !fnmatch((a), (b), 0)
6370
6371/*
6372 * Prepare a pattern for a expmeta (internal glob(3)) call.
6373 *
6374 * Returns an stalloced string.
6375 */
6376static char *
Ron Yorston549deab2015-05-18 09:57:51 +02006377preglob(const char *pattern, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006378{
Denys Vlasenko740058b2018-01-09 17:01:00 +01006379 return rmescapes((char *)pattern, flag | RMESCAPE_GLOB, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006380}
6381
6382/*
6383 * Put a string on the stack.
6384 */
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006385static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006386memtodest(const char *p, size_t len, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006387{
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006388 int syntax = flags & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006389 char *q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006390 char *s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006391
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006392 if (!len)
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006393 return 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006394
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006395 q = makestrspace(len * 2, expdest);
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006396 s = q;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006397
6398 do {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006399 unsigned char c = *p++;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006400 if (c) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006401 if (flags & QUOTES_ESC) {
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006402 int n = SIT(c, syntax);
6403 if (n == CCTL
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006404 || ((flags & EXP_QUOTED) && n == CBACK)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006405 ) {
6406 USTPUTC(CTLESC, q);
6407 }
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02006408 }
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006409 } else if (!(flags & EXP_KEEPNUL))
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006410 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006411 USTPUTC(c, q);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006412 } while (--len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006413
6414 expdest = q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006415 return q - s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006416}
6417
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006418static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006419strtodest(const char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006420{
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006421 size_t len = strlen(p);
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006422 memtodest(p, len, flags);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006423 return len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006424}
6425
6426/*
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006427 * Our own itoa().
6428 * cvtnum() is used even if math support is off (to prepare $? values and such).
6429 */
6430static int
6431cvtnum(arith_t num, int flags)
6432{
6433 /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
6434 /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
6435 int len = (sizeof(arith_t) >= 4) ? sizeof(arith_t) * 3 : sizeof(arith_t) * 3 + 2;
6436 char buf[len];
6437
6438 len = fmtstr(buf, len, ARITH_FMT, num);
6439 return memtodest(buf, len, flags);
6440}
6441
6442/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 * Record the fact that we have to scan this region of the
6444 * string for IFS characters.
6445 */
6446static void
6447recordregion(int start, int end, int nulonly)
6448{
6449 struct ifsregion *ifsp;
6450
6451 if (ifslastp == NULL) {
6452 ifsp = &ifsfirst;
6453 } else {
6454 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006455 ifsp = ckzalloc(sizeof(*ifsp));
6456 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006457 ifslastp->next = ifsp;
6458 INT_ON;
6459 }
6460 ifslastp = ifsp;
6461 ifslastp->begoff = start;
6462 ifslastp->endoff = end;
6463 ifslastp->nulonly = nulonly;
6464}
6465
6466static void
6467removerecordregions(int endoff)
6468{
6469 if (ifslastp == NULL)
6470 return;
6471
6472 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006473 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006474 struct ifsregion *ifsp;
6475 INT_OFF;
6476 ifsp = ifsfirst.next->next;
6477 free(ifsfirst.next);
6478 ifsfirst.next = ifsp;
6479 INT_ON;
6480 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006481 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006482 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006483 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006484 ifslastp = &ifsfirst;
6485 ifsfirst.endoff = endoff;
6486 }
6487 return;
6488 }
6489
6490 ifslastp = &ifsfirst;
6491 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006492 ifslastp = ifslastp->next;
6493 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006494 struct ifsregion *ifsp;
6495 INT_OFF;
6496 ifsp = ifslastp->next->next;
6497 free(ifslastp->next);
6498 ifslastp->next = ifsp;
6499 INT_ON;
6500 }
6501 if (ifslastp->endoff > endoff)
6502 ifslastp->endoff = endoff;
6503}
6504
6505static char *
Denys Vlasenko82331882020-02-24 10:02:50 +01006506exptilde(char *startp, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006507{
Denys Vlasenkocd716832009-11-28 22:14:02 +01006508 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006509 char *name;
6510 struct passwd *pw;
6511 const char *home;
Denys Vlasenko82331882020-02-24 10:02:50 +01006512 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006513
Denys Vlasenko82331882020-02-24 10:02:50 +01006514 p = startp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006515 name = p + 1;
6516
6517 while ((c = *++p) != '\0') {
6518 switch (c) {
6519 case CTLESC:
6520 return startp;
6521 case CTLQUOTEMARK:
6522 return startp;
6523 case ':':
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006524 if (flag & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006525 goto done;
6526 break;
6527 case '/':
6528 case CTLENDVAR:
6529 goto done;
6530 }
6531 }
6532 done:
Denys Vlasenko82331882020-02-24 10:02:50 +01006533 if (flag & EXP_DISCARD)
6534 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006535 *p = '\0';
6536 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02006537 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006538 } else {
6539 pw = getpwnam(name);
Denys Vlasenko5fe20cf2022-03-01 10:08:59 +01006540 home = pw ? pw->pw_dir : NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006541 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006542 *p = c;
Denys Vlasenkoe880b1f2020-02-16 18:31:05 +01006543 if (!home)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544 goto lose;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006545 strtodest(home, flag | EXP_QUOTED);
Denys Vlasenko82331882020-02-24 10:02:50 +01006546 out:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006547 return p;
6548 lose:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006549 return startp;
6550}
6551
6552/*
6553 * Execute a command inside back quotes. If it's a builtin command, we
6554 * want to save its output in a block obtained from malloc. Otherwise
6555 * we fork off a subprocess and get the output of the command via a pipe.
6556 * Should be called with interrupts off.
6557 */
6558struct backcmd { /* result of evalbackcmd */
6559 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006560 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006561 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006562 struct job *jp; /* job structure for command */
6563};
6564
6565/* These forward decls are needed to use "eval" code for backticks handling: */
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +02006566/* flags in argument to evaltree */
6567#define EV_EXIT 01 /* exit after evaluating tree */
6568#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02006569static int evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006570
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006571/* An evaltree() which is known to never return.
6572 * Used to use an alias:
6573 * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__));
6574 * but clang was reported to "transfer" noreturn-ness to evaltree() as well.
6575 */
6576static ALWAYS_INLINE NORETURN void
6577evaltreenr(union node *n, int flags)
6578{
6579 evaltree(n, flags);
6580 bb_unreachable(abort());
6581 /* NOTREACHED */
6582}
6583
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02006584static void FAST_FUNC
Ron Yorstona1b0d382020-07-23 08:32:27 +01006585evalbackcmd(union node *n, struct backcmd *result
6586 IF_BASH_PROCESS_SUBST(, int ctl))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006587{
Denys Vlasenko579ad102016-10-25 21:10:20 +02006588 int pip[2];
6589 struct job *jp;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006590#if BASH_PROCESS_SUBST
6591 /* determine end of pipe used by parent (ip) and child (ic) */
6592 const int ip = (ctl == CTLTOPROC);
6593 const int ic = !(ctl == CTLTOPROC);
6594#else
6595 const int ctl = CTLBACKQ;
6596 const int ip = 0;
6597 const int ic = 1;
6598#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006599
6600 result->fd = -1;
6601 result->buf = NULL;
6602 result->nleft = 0;
6603 result->jp = NULL;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006604 if (n == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006605 goto out;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006606 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006607
Denys Vlasenko579ad102016-10-25 21:10:20 +02006608 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02006609 ash_msg_and_raise_perror("can't create pipe");
Ron Yorstona1b0d382020-07-23 08:32:27 +01006610 /* process substitution uses NULL job/node, like openhere() */
6611 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
6612 if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02006613 /* child */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006614 FORCE_INT_ON;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006615 close(pip[ip]);
6616 /* ic is index of child end of pipe *and* fd to connect it to */
6617 if (pip[ic] != ic) {
6618 /*close(ic);*/
6619 dup2_or_raise(pip[ic], ic);
6620 close(pip[ic]);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006621 }
Denys Vlasenko960ca382016-10-25 18:12:15 +02006622/* TODO: eflag clearing makes the following not abort:
6623 * ash -c 'set -e; z=$(false;echo foo); echo $z'
6624 * which is what bash does (unless it is in POSIX mode).
6625 * dash deleted "eflag = 0" line in the commit
6626 * Date: Mon, 28 Jun 2010 17:11:58 +1000
6627 * [EVAL] Don't clear eflag in evalbackcmd
6628 * For now, preserve bash-like behavior, it seems to be somewhat more useful:
6629 */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006630 eflag = 0;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006631 ifsfree();
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006632 evaltreenr(n, EV_EXIT);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006633 /* NOTREACHED */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006634 }
Denys Vlasenko70392332016-10-27 02:31:55 +02006635 /* parent */
Ron Yorstona1b0d382020-07-23 08:32:27 +01006636#if BASH_PROCESS_SUBST
6637 if (ctl != CTLBACKQ) {
6638 int fd = fcntl(pip[ip], F_DUPFD, 64);
6639 if (fd > 0) {
6640 close(pip[ip]);
6641 pip[ip] = fd;
6642 }
6643 pushfd(pip[ip]);
6644 }
6645#endif
6646 close(pip[ic]);
6647 result->fd = pip[ip];
Denys Vlasenko579ad102016-10-25 21:10:20 +02006648 result->jp = jp;
6649
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006650 out:
6651 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
6652 result->fd, result->buf, result->nleft, result->jp));
6653}
6654
6655/*
6656 * Expand stuff in backwards quotes.
6657 */
6658static void
Ron Yorstona1b0d382020-07-23 08:32:27 +01006659expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006660{
Ron Yorstona1b0d382020-07-23 08:32:27 +01006661#if !BASH_PROCESS_SUBST
6662 const int ctl = CTLBACKQ;
6663#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006664 struct backcmd in;
6665 int i;
6666 char buf[128];
6667 char *p;
6668 char *dest;
6669 int startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006670 struct stackmark smark;
6671
Denys Vlasenko82331882020-02-24 10:02:50 +01006672 if (flag & EXP_DISCARD)
6673 goto out;
6674
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006675 INT_OFF;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02006676 startloc = expdest - (char *)stackblock();
6677 pushstackmark(&smark, startloc);
Ron Yorstona1b0d382020-07-23 08:32:27 +01006678 evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006679 popstackmark(&smark);
6680
Ron Yorstona1b0d382020-07-23 08:32:27 +01006681 if (ctl != CTLBACKQ) {
6682 sprintf(buf, DEV_FD_PREFIX"%d", in.fd);
6683 strtodest(buf, BASESYNTAX);
6684 goto done;
6685 }
6686
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006687 p = in.buf;
6688 i = in.nleft;
6689 if (i == 0)
6690 goto read;
6691 for (;;) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006692 memtodest(p, i, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006693 read:
6694 if (in.fd < 0)
6695 break;
Ron Yorston61d6ae22015-04-19 10:50:25 +01006696 i = nonblock_immune_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006697 TRACE(("expbackq: read returns %d\n", i));
6698 if (i <= 0)
6699 break;
6700 p = buf;
6701 }
6702
Denis Vlasenko60818682007-09-28 22:07:23 +00006703 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006704 if (in.fd >= 0) {
6705 close(in.fd);
6706 back_exitstatus = waitforjob(in.jp);
6707 }
Ron Yorstona1b0d382020-07-23 08:32:27 +01006708 done:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006709 INT_ON;
6710
6711 /* Eat all trailing newlines */
6712 dest = expdest;
Denys Vlasenko9ee58922020-02-17 10:24:32 +01006713 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006714 STUNPUTC(dest);
6715 expdest = dest;
6716
Ron Yorston549deab2015-05-18 09:57:51 +02006717 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006718 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006719 TRACE(("evalbackq: size:%d:'%.*s'\n",
6720 (int)((dest - (char *)stackblock()) - startloc),
6721 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006722 stackblock() + startloc));
Denys Vlasenko82331882020-02-24 10:02:50 +01006723
6724 out:
6725 argbackq = argbackq->next;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006726}
6727
Denys Vlasenko82331882020-02-24 10:02:50 +01006728/* expari needs it */
6729static char *argstr(char *p, int flag);
6730
Denys Vlasenko0b883582016-12-23 16:49:07 +01006731#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006732/*
6733 * Expand arithmetic expression. Backup to start of expression,
6734 * evaluate, place result in (backed up) result, adjust string position.
6735 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006736static char *
6737expari(char *start, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006738{
Denys Vlasenko82331882020-02-24 10:02:50 +01006739 struct stackmark sm;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006740 int begoff;
Denys Vlasenko82331882020-02-24 10:02:50 +01006741 int endoff;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006742 int len;
Denys Vlasenko82331882020-02-24 10:02:50 +01006743 arith_t result;
6744 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006745
Denys Vlasenko82331882020-02-24 10:02:50 +01006746 p = stackblock();
6747 begoff = expdest - p;
6748 p = argstr(start, flag & EXP_DISCARD);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006749
Denys Vlasenko82331882020-02-24 10:02:50 +01006750 if (flag & EXP_DISCARD)
6751 goto out;
6752
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006753 start = stackblock();
Denys Vlasenko82331882020-02-24 10:02:50 +01006754 endoff = expdest - start;
6755 start += begoff;
6756 STADJUST(start - expdest, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006757
6758 removerecordregions(begoff);
6759
Ron Yorston549deab2015-05-18 09:57:51 +02006760 if (flag & QUOTES_ESC)
Denys Vlasenko82331882020-02-24 10:02:50 +01006761 rmescapes(start, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006762
Denys Vlasenko82331882020-02-24 10:02:50 +01006763 pushstackmark(&sm, endoff);
6764 result = ash_arith(start);
6765 popstackmark(&sm);
6766
6767 len = cvtnum(result, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006768
Ron Yorston549deab2015-05-18 09:57:51 +02006769 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006770 recordregion(begoff, begoff + len, 0);
Denys Vlasenko82331882020-02-24 10:02:50 +01006771
6772 out:
6773 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006774}
6775#endif
6776
6777/* argstr needs it */
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02006778static char *evalvar(char *p, int flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006779
6780/*
6781 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6782 * characters to allow for further processing. Otherwise treat
6783 * $@ like $* since no splitting will be performed.
6784 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006785static char *
Denys Vlasenko7f198482020-02-24 09:57:08 +01006786argstr(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006787{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006788 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006789 '=',
6790 ':',
6791 CTLQUOTEMARK,
6792 CTLENDVAR,
6793 CTLESC,
6794 CTLVAR,
6795 CTLBACKQ,
Ron Yorstona1b0d382020-07-23 08:32:27 +01006796#if BASH_PROCESS_SUBST
6797 CTLTOPROC,
6798 CTLFROMPROC,
6799#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01006800#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006801 CTLARI,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006802 CTLENDARI,
6803#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006804 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006805 };
6806 const char *reject = spclchars;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006807 int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006808 int inquotes;
6809 size_t length;
6810 int startloc;
6811
Denys Vlasenko82331882020-02-24 10:02:50 +01006812 reject += !!(flag & EXP_VARTILDE2);
6813 reject += flag & EXP_VARTILDE ? 0 : 2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006814 inquotes = 0;
6815 length = 0;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006816 if (flag & EXP_TILDE) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006817 flag &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006818 tilde:
Denys Vlasenko82331882020-02-24 10:02:50 +01006819 if (*p == '~')
6820 p = exptilde(p, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006821 }
6822 start:
6823 startloc = expdest - (char *)stackblock();
6824 for (;;) {
Denys Vlasenko82331882020-02-24 10:02:50 +01006825 int end;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006826 unsigned char c;
6827
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006828 length += strcspn(p + length, reject);
Denys Vlasenko82331882020-02-24 10:02:50 +01006829 end = 0;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006830 c = p[length];
Denys Vlasenko82331882020-02-24 10:02:50 +01006831 if (!(c & 0x80)
6832 IF_FEATURE_SH_MATH(|| c == CTLENDARI)
6833 || c == CTLENDVAR
6834 ) {
6835 /*
6836 * c == '=' || c == ':' || c == '\0' ||
6837 * c == CTLENDARI || c == CTLENDVAR
6838 */
6839 length++;
6840 /* c == '\0' || c == CTLENDARI || c == CTLENDVAR */
6841 end = !!((c - 1) & 0x80);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006842 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006843 if (length > 0 && !(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006844 int newloc;
Denys Vlasenko82331882020-02-24 10:02:50 +01006845 char *q;
6846
6847 q = stnputs(p, length, expdest);
6848 q[-1] &= end - 1;
6849 expdest = q - (flag & EXP_WORD ? end : 0);
6850 newloc = q - (char *)stackblock() - end;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006851 if (breakall && !inquotes && newloc > startloc) {
6852 recordregion(startloc, newloc, 0);
6853 }
6854 startloc = newloc;
6855 }
6856 p += length + 1;
6857 length = 0;
6858
Denys Vlasenko82331882020-02-24 10:02:50 +01006859 if (end)
6860 break;
6861
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006862 switch (c) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006863 case '=':
Denys Vlasenko7f198482020-02-24 09:57:08 +01006864 flag |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006865 reject++;
6866 /* fall through */
6867 case ':':
6868 /*
6869 * sort of a hack - expand tildes in variable
6870 * assignments (after the first '=' and after ':'s).
6871 */
6872 if (*--p == '~') {
6873 goto tilde;
6874 }
6875 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006876 case CTLQUOTEMARK:
6877 /* "$@" syntax adherence hack */
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006878 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006879 p = evalvar(p + 1, flag | EXP_QUOTED) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006880 goto start;
6881 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006882 inquotes ^= EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006883 addquote:
Denys Vlasenko7f198482020-02-24 09:57:08 +01006884 if (flag & QUOTES_ESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006885 p--;
6886 length++;
6887 startloc++;
6888 }
6889 break;
6890 case CTLESC:
6891 startloc++;
6892 length++;
6893 goto addquote;
6894 case CTLVAR:
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006895 TRACE(("argstr: evalvar('%s')\n", p));
Denys Vlasenko7f198482020-02-24 09:57:08 +01006896 p = evalvar(p, flag | inquotes);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006897 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006898 goto start;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006899#if BASH_PROCESS_SUBST
6900 case CTLTOPROC:
6901 case CTLFROMPROC:
6902#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006903 case CTLBACKQ:
Ron Yorstona1b0d382020-07-23 08:32:27 +01006904 expbackq(argbackq->n, flag | inquotes IF_BASH_PROCESS_SUBST(, c));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006905 goto start;
Denys Vlasenko0b883582016-12-23 16:49:07 +01006906#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006907 case CTLARI:
6908 p = expari(p, flag | inquotes);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006909 goto start;
6910#endif
6911 }
6912 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006913 return p - 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006914}
6915
6916static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006917scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6918 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006919{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006920 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006921 char c;
6922
6923 loc = startp;
6924 loc2 = rmesc;
6925 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006926 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006927 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006928
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006929 c = *loc2;
6930 if (zero) {
6931 *loc2 = '\0';
6932 s = rmesc;
6933 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006934 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006935
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006936 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006937 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006939 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006940 loc++;
6941 loc++;
6942 loc2++;
6943 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006944 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006945}
6946
6947static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006948scanright(char *startp, char *rmesc, char *rmescend,
6949 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006950{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006951#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6952 int try2optimize = match_at_start;
6953#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006954 int esc = 0;
6955 char *loc;
6956 char *loc2;
6957
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006958 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6959 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6960 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6961 * Logic:
6962 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6963 * and on each iteration they go back two/one char until they reach the beginning.
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02006964 * We try to match "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6965 * If one of these matches, return pointer past last matched char in startp.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006966 */
6967 /* TODO: document in what other circumstances we are called. */
6968
6969 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006970 int match;
6971 char c = *loc2;
6972 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006973 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006974 *loc2 = '\0';
6975 s = rmesc;
6976 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006977 match = pmatch(pattern, s);
6978 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006979 *loc2 = c;
6980 if (match)
6981 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006982#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6983 if (try2optimize) {
6984 /* Maybe we can optimize this:
6985 * if pattern ends with unescaped *, we can avoid checking
Denys Vlasenko10ad6222017-04-17 16:13:32 +02006986 * shorter strings: if "foo*" doesn't match "raw_value_of_v",
6987 * it won't match truncated "raw_value_of_" strings too.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006988 */
6989 unsigned plen = strlen(pattern);
6990 /* Does it end with "*"? */
6991 if (plen != 0 && pattern[--plen] == '*') {
6992 /* "xxxx*" is not escaped */
6993 /* "xxx\*" is escaped */
6994 /* "xx\\*" is not escaped */
6995 /* "x\\\*" is escaped */
6996 int slashes = 0;
6997 while (plen != 0 && pattern[--plen] == '\\')
6998 slashes++;
6999 if (!(slashes & 1))
7000 break; /* ends with unescaped "*" */
7001 }
7002 try2optimize = 0;
7003 }
7004#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007005 loc--;
7006 if (quotes) {
7007 if (--esc < 0) {
7008 esc = esclen(startp, loc);
7009 }
7010 if (esc % 2) {
7011 esc--;
7012 loc--;
7013 }
7014 }
7015 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02007016 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007017}
7018
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007019static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007020static void
7021varunset(const char *end, const char *var, const char *umsg, int varflags)
7022{
7023 const char *msg;
7024 const char *tail;
7025
7026 tail = nullstr;
7027 msg = "parameter not set";
7028 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007029 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007030 if (varflags & VSNUL)
7031 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007032 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007033 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007034 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007035 }
Denys Vlasenko1c545522022-08-02 11:13:44 +02007036 ifsfree();
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02007037 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007038}
7039
Denys Vlasenko82331882020-02-24 10:02:50 +01007040static char *
7041subevalvar(char *start, char *str, int strloc,
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007042 int startloc, int varflags, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007043{
Denys Vlasenko82331882020-02-24 10:02:50 +01007044 int subtype = varflags & VSTYPE;
Ron Yorston549deab2015-05-18 09:57:51 +02007045 int quotes = flag & QUOTES_ESC;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007046 char *startp;
7047 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007048 char *rmesc, *rmescend;
Denys Vlasenko82331882020-02-24 10:02:50 +01007049 long amount;
7050 int resetloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007051 int argstr_flags;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007052 IF_BASH_PATTERN_SUBST(int workloc;)
Denys Vlasenko740058b2018-01-09 17:01:00 +01007053 IF_BASH_PATTERN_SUBST(int slash_pos;)
7054 IF_BASH_PATTERN_SUBST(char *repl;)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007055 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007056 char *(*scan)(char*, char*, char*, char*, int, int);
Denys Vlasenko82331882020-02-24 10:02:50 +01007057 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007058
Denys Vlasenko82331882020-02-24 10:02:50 +01007059 //bb_error_msg("subevalvar(start:'%s',str:'%s',strloc:%d,startloc:%d,varflags:%x,quotes:%d)",
7060 // start, str, strloc, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007061
Denys Vlasenko740058b2018-01-09 17:01:00 +01007062#if BASH_PATTERN_SUBST
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007063 /* For "${v/pattern/repl}", we must find the delimiter _before_
7064 * argstr() call expands possible variable references in pattern:
7065 * think about "v=a; a=a/; echo ${v/$a/r}" case.
7066 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007067 repl = NULL;
7068 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
7069 /* Find '/' and replace with NUL */
Denys Vlasenko82331882020-02-24 10:02:50 +01007070 repl = start;
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02007071 /* The pattern can't be empty.
7072 * IOW: if the first char after "${v//" is a slash,
7073 * it does not terminate the pattern - it's the first char of the pattern:
7074 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
7075 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
7076 */
7077 if (*repl == '/')
7078 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007079 for (;;) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007080 if (*repl == '\0') {
7081 repl = NULL;
7082 break;
7083 }
7084 if (*repl == '/') {
7085 *repl = '\0';
7086 break;
7087 }
Sören Tempelfa52ac92022-02-28 08:36:50 +01007088 if ((unsigned char)*repl == CTLENDVAR) { /* ${v/pattern} (no trailing /, no repl) */
7089 repl = NULL;
7090 break;
7091 }
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02007092 /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007093 if ((unsigned char)*repl == CTLESC && repl[1])
Denys Vlasenko740058b2018-01-09 17:01:00 +01007094 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007095 repl++;
7096 }
7097 }
7098#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007099 argstr_flags = (flag & EXP_DISCARD) | EXP_TILDE;
7100 if (!str
Denys Vlasenko216913c2018-04-02 12:35:04 +02007101#if BASH_SUBSTR
7102 && subtype != VSSUBSTR
7103#endif
7104 ) {
7105 /* EXP_CASE keeps CTLESC's */
Denys Vlasenko82331882020-02-24 10:02:50 +01007106 argstr_flags |= EXP_CASE;
Denys Vlasenko216913c2018-04-02 12:35:04 +02007107 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007108 p = argstr(start, argstr_flags);
7109
Denys Vlasenko216913c2018-04-02 12:35:04 +02007110 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
Denys Vlasenko740058b2018-01-09 17:01:00 +01007111#if BASH_PATTERN_SUBST
7112 slash_pos = -1;
7113 if (repl) {
7114 slash_pos = expdest - ((char *)stackblock() + strloc);
Denys Vlasenko883cdb72021-01-09 08:27:37 +01007115 if (!(flag & EXP_DISCARD))
7116 STPUTC('/', expdest);
Denys Vlasenko216913c2018-04-02 12:35:04 +02007117 //bb_error_msg("repl+1:'%s'", repl + 1);
Denys Vlasenko82331882020-02-24 10:02:50 +01007118 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007119 *repl = '/';
7120 }
7121#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007122 if (flag & EXP_DISCARD)
7123 return p;
7124
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007125 startp = (char *)stackblock() + startloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007126 //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007127
7128 switch (subtype) {
7129 case VSASSIGN:
Denys Vlasenko7f198482020-02-24 09:57:08 +01007130 setvar0(str, startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007131
7132 loc = startp;
7133 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007134
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007135 case VSQUESTION:
Denys Vlasenko82331882020-02-24 10:02:50 +01007136 varunset(start, str, startp, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007137 /* NOTREACHED */
7138
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007139#if BASH_SUBSTR
Denys Vlasenko826360f2017-07-17 17:49:11 +02007140 case VSSUBSTR: {
7141 int pos, len, orig_len;
7142 char *colon;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007143 char *vstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007144
Denys Vlasenko7f198482020-02-24 09:57:08 +01007145 loc = vstr = stackblock() + strloc;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007146
Denys Vlasenko826360f2017-07-17 17:49:11 +02007147 /* Read POS in ${var:POS:LEN} */
7148 colon = strchr(loc, ':');
7149 if (colon) *colon = '\0';
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007150 pos = substr_atoi(loc);
Denys Vlasenko826360f2017-07-17 17:49:11 +02007151 if (colon) *colon = ':';
7152
7153 /* Read LEN in ${var:POS:LEN} */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007154 len = vstr - startp - 1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007155 /* *loc != '\0', guaranteed by parser */
7156 if (quotes) {
7157 char *ptr;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007158 /* Adjust the length by the number of escapes */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007159 for (ptr = startp; ptr < (vstr - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007160 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007161 len--;
7162 ptr++;
7163 }
7164 }
7165 }
7166 orig_len = len;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007167 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007168 /* ${var::LEN} */
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007169 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007170 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007171 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007172 len = orig_len;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007173 while (*loc && *loc != ':')
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007174 loc++;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007175 if (*loc++ == ':')
7176 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007177 }
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007178 if (pos < 0) {
7179 /* ${VAR:$((-n)):l} starts n chars from the end */
7180 pos = orig_len + pos;
7181 }
7182 if ((unsigned)pos >= orig_len) {
7183 /* apart from obvious ${VAR:999999:l},
7184 * covers ${VAR:$((-9999999)):l} - result is ""
Denys Vlasenko826360f2017-07-17 17:49:11 +02007185 * (bash compat)
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007186 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007187 pos = 0;
7188 len = 0;
7189 }
Denys Vlasenko826360f2017-07-17 17:49:11 +02007190 if (len < 0) {
7191 /* ${VAR:N:-M} sets LEN to strlen()-M */
7192 len = (orig_len - pos) + len;
7193 }
7194 if ((unsigned)len > (orig_len - pos))
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007195 len = orig_len - pos;
7196
Alin Mr21e8dbf2021-07-28 11:40:01 +03007197 if (!quotes) {
Denys Vlasenko7750b5a2022-03-01 09:56:54 +01007198 /* want: loc = mempcpy(startp, startp + pos, len)
7199 * but it does not allow overlapping arguments */
7200 loc = startp;
7201 while (--len >= 0) {
7202 *loc = loc[pos];
7203 loc++;
7204 }
Alin Mr21e8dbf2021-07-28 11:40:01 +03007205 } else {
7206 for (vstr = startp; pos != 0; pos--) {
7207 if ((unsigned char)*vstr == CTLESC)
7208 vstr++;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007209 vstr++;
Alin Mr21e8dbf2021-07-28 11:40:01 +03007210 }
7211 for (loc = startp; len != 0; len--) {
7212 if ((unsigned char)*vstr == CTLESC)
7213 *loc++ = *vstr++;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007214 *loc++ = *vstr++;
Alin Mr21e8dbf2021-07-28 11:40:01 +03007215 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007216 }
7217 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007218 goto out;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007219 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007220#endif /* BASH_SUBSTR */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007221 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007222
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007223 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007224
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007225#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01007226 repl = NULL;
7227
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007228 /* We'll comeback here if we grow the stack while handling
7229 * a VSREPLACE or VSREPLACEALL, since our pointers into the
7230 * stack will need rebasing, and we'll need to remove our work
7231 * areas each time
7232 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007233 restart:
7234#endif
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007235
7236 amount = expdest - ((char *)stackblock() + resetloc);
7237 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007238 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007239
7240 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007241 rmescend = (char *)stackblock() + strloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007242 //bb_error_msg("str7:'%s'", rmescend);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007243 if (quotes) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007244//TODO: how to handle slash_pos here if string changes (shortens?)
7245 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007246 if (rmesc != startp) {
7247 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007248 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007249 }
7250 }
7251 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007252 str = (char *)stackblock() + strloc;
Ron Yorston417622c2015-05-18 09:59:14 +02007253 /*
7254 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
7255 * The result is a_\_z_c (not a\_\_z_c)!
7256 *
7257 * The search pattern and replace string treat backslashes differently!
Denys Vlasenko740058b2018-01-09 17:01:00 +01007258 * "&slash_pos" causes rmescapes() to work differently on the pattern
Ron Yorston417622c2015-05-18 09:59:14 +02007259 * and string. It's only used on the first call.
7260 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007261 //bb_error_msg("str8:'%s' slash_pos:%d", str, slash_pos);
7262 rmescapes(str, RMESCAPE_GLOB,
7263 repl ? NULL : (slash_pos < 0 ? NULL : &slash_pos)
7264 );
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007265
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007266#if BASH_PATTERN_SUBST
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02007267 workloc = expdest - (char *)stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007268 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007269 size_t no_meta_len, first_escaped;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007270 int len;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007271 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007272
Denis Vlasenkod6855d12008-09-27 14:03:25 +00007273 if (!repl) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007274 //bb_error_msg("str9:'%s' slash_pos:%d", str, slash_pos);
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007275 repl = nullstr;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007276 if (slash_pos >= 0) {
7277 repl = str + slash_pos;
Ron Yorston417622c2015-05-18 09:59:14 +02007278 *repl++ = '\0';
Denys Vlasenko740058b2018-01-09 17:01:00 +01007279 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007280 }
Ron Yorston417622c2015-05-18 09:59:14 +02007281 //bb_error_msg("str:'%s' repl:'%s'", str, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007282
7283 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007284 if (str[0] == '\0')
Denys Vlasenko82331882020-02-24 10:02:50 +01007285 goto out1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007286
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007287 first_escaped = (str[0] == '\\' && str[1]);
7288 /* "first_escaped" trick allows to treat e.g. "\*no_glob_chars"
7289 * as literal too (as it is semi-common, and easy to accomodate
7290 * by just using str + 1).
7291 */
7292 no_meta_len = strpbrk(str + first_escaped * 2, "*?[\\") ? 0 : strlen(str);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007293 len = 0;
7294 idx = startp;
7295 end = str - 1;
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007296 while (idx <= end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007297 try_to_match:
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007298 if (no_meta_len == 0) {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007299 /* pattern has meta chars, have to glob */
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007300 loc = scanright(idx, rmesc, rmescend, str, quotes, /*match_at_start:*/ 1);
7301 } else {
7302 /* Testcase for very slow replace (performs about 22k replaces):
7303 * x=::::::::::::::::::::::
7304 * x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;echo ${#x}
7305 * echo "${x//:/|}"
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007306 * To test "first_escaped" logic, replace : with *.
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007307 */
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007308 if (strncmp(rmesc, str + first_escaped, no_meta_len - first_escaped) != 0)
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007309 goto no_match;
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007310 loc = idx;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007311 if (!quotes) {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007312 loc += no_meta_len - first_escaped;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007313 } else {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007314 size_t n = no_meta_len - first_escaped;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007315 do {
7316 if ((unsigned char)*loc == CTLESC)
7317 loc++;
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007318 loc++;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007319 } while (--n != 0);
7320 }
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007321 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007322 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007323 if (!loc) {
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007324 char *restart_detect;
7325 no_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007326 /* No match, advance */
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007327 restart_detect = stackblock();
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007328 skip_matching:
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007329 if (idx >= end)
7330 break;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007331 STPUTC(*idx, expdest);
Denys Vlasenkodaa66ed2022-08-02 12:41:18 +02007332 if (stackblock() != restart_detect)
7333 goto restart;
Denys Vlasenkocd716832009-11-28 22:14:02 +01007334 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007335 idx++;
7336 len++;
7337 STPUTC(*idx, expdest);
Denys Vlasenkodaa66ed2022-08-02 12:41:18 +02007338 if (stackblock() != restart_detect)
7339 goto restart;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007340 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007341 idx++;
7342 len++;
7343 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007344 /* continue; - prone to quadratic behavior, smarter code: */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007345 if (str[0] == '*') {
7346 /* Pattern is "*foo". If "*foo" does not match "long_string",
7347 * it would never match "ong_string" etc, no point in trying.
7348 */
7349 goto skip_matching;
7350 }
7351 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007352 }
7353
7354 if (subtype == VSREPLACEALL) {
7355 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007356 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007357 idx++;
7358 idx++;
7359 rmesc++;
7360 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007361 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007362 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007363 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007364
Sören Tempel7c2a3bd2022-08-02 18:23:32 +02007365 /* The STPUTC invocations above may resize and move the
7366 * stack via realloc(3). Since repl is a pointer into the
7367 * stack, we need to reconstruct it relative to stackblock().
7368 */
7369 if (slash_pos >= 0)
7370 repl = (char *)stackblock() + strloc + slash_pos + 1;
7371
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007372 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007373 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007374 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007375 if (quotes && *loc == '\\') {
7376 STPUTC(CTLESC, expdest);
7377 len++;
7378 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007379 STPUTC(*loc, expdest);
7380 if (stackblock() != restart_detect)
7381 goto restart;
7382 len++;
7383 }
7384
7385 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02007386 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007387 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007388 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007389 STPUTC(*idx, expdest);
7390 if (stackblock() != restart_detect)
7391 goto restart;
7392 len++;
7393 idx++;
7394 }
7395 break;
7396 }
7397 }
7398
7399 /* We've put the replaced text into a buffer at workloc, now
7400 * move it to the right place and adjust the stack.
7401 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007402 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007403 startp = (char *)stackblock() + startloc;
7404 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007405 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007406 loc = startp + len;
7407 goto out;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007408 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007409#endif /* BASH_PATTERN_SUBST */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007410
7411 subtype -= VSTRIMRIGHT;
7412#if DEBUG
7413 if (subtype < 0 || subtype > 7)
7414 abort();
7415#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007416 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007417 zero = subtype >> 1;
7418 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
7419 scan = (subtype & 1) ^ zero ? scanleft : scanright;
7420
7421 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
7422 if (loc) {
7423 if (zero) {
7424 memmove(startp, loc, str - loc);
7425 loc = startp + (str - loc) - 1;
7426 }
7427 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007428 } else
7429 loc = str - 1;
7430
7431 out:
7432 amount = loc - expdest;
7433 STADJUST(amount, expdest);
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007434#if BASH_PATTERN_SUBST
Denys Vlasenko82331882020-02-24 10:02:50 +01007435 out1:
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007436#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007437 /* Remove any recorded regions beyond start of variable */
7438 removerecordregions(startloc);
7439
7440 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007441}
7442
7443/*
7444 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007445 * name parameter (examples):
7446 * ash -c 'echo $1' name:'1='
7447 * ash -c 'echo $qwe' name:'qwe='
7448 * ash -c 'echo $$' name:'$='
7449 * ash -c 'echo ${$}' name:'$='
7450 * ash -c 'echo ${$##q}' name:'$=q'
7451 * ash -c 'echo ${#$}' name:'$='
7452 * note: examples with bad shell syntax:
7453 * ash -c 'echo ${#$1}' name:'$=1'
7454 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007455 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02007456static NOINLINE ssize_t
Denys Vlasenko440da972018-08-05 14:29:58 +02007457varvalue(char *name, int varflags, int flags, int quoted)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007458{
Mike Frysinger98c52642009-04-02 10:02:37 +00007459 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007460 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007461 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007462 ssize_t len = 0;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007463 int sep;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007464 int subtype = varflags & VSTYPE;
Denys Vlasenko82331882020-02-24 10:02:50 +01007465 int discard = (subtype == VSPLUS || subtype == VSLENGTH) | (flags & EXP_DISCARD);
7466
7467 if (!subtype) {
7468 if (discard)
7469 return -1;
7470
Denys Vlasenko1c545522022-08-02 11:13:44 +02007471 ifsfree();
Denys Vlasenko82331882020-02-24 10:02:50 +01007472 raise_error_syntax("bad substitution");
7473 }
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007474
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007475 flags |= EXP_KEEPNUL;
7476 flags &= discard ? ~QUOTES_ESC : ~0;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007477 sep = (flags & EXP_FULL) << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007478
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007479 switch (*name) {
7480 case '$':
7481 num = rootpid;
7482 goto numvar;
7483 case '?':
7484 num = exitstatus;
7485 goto numvar;
7486 case '#':
7487 num = shellparam.nparam;
7488 goto numvar;
7489 case '!':
7490 num = backgndpid;
7491 if (num == 0)
7492 return -1;
7493 numvar:
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007494 len = cvtnum(num, flags);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007495 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007496 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00007497 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007498 for (i = NOPTS - 1; i >= 0; i--) {
Martijn Dekkerad4e9612018-03-31 18:15:59 +02007499 if (optlist[i] && optletters(i)) {
Mike Frysinger98c52642009-04-02 10:02:37 +00007500 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007501 len++;
7502 }
7503 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007504 check_1char_name:
7505#if 0
7506 /* handles cases similar to ${#$1} */
7507 if (name[2] != '\0')
7508 raise_error_syntax("bad substitution");
7509#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007510 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007511 case '@':
7512 if (quoted && sep)
7513 goto param;
7514 /* fall through */
7515 case '*': {
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007516 char **ap;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007517 char sepc;
Denys Vlasenko440da972018-08-05 14:29:58 +02007518 char c;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007519
Denys Vlasenko440da972018-08-05 14:29:58 +02007520 /* We will set c to 0 or ~0 depending on whether
7521 * we're doing field splitting. We won't do field
7522 * splitting if either we're quoted or sep is zero.
7523 *
7524 * Instead of testing (quoted || !sep) the following
7525 * trick optimises away any branches by using the
7526 * fact that EXP_QUOTED (which is the only bit that
7527 * can be set in quoted) is the same as EXP_FULL <<
7528 * CHAR_BIT (which is the only bit that can be set
7529 * in sep).
7530 */
7531#if EXP_QUOTED >> CHAR_BIT != EXP_FULL
7532#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT
7533#endif
7534 c = !((quoted | ~sep) & EXP_QUOTED) - 1;
7535 sep &= ~quoted;
7536 sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007537 param:
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007538 sepc = sep;
Denys Vlasenko0dd8e452016-10-01 21:02:06 +02007539 ap = shellparam.p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007540 if (!ap)
7541 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007542 while ((p = *ap++) != NULL) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007543 len += strtodest(p, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007544
7545 if (*ap && sep) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007546 len++;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007547 memtodest(&sepc, 1, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007548 }
7549 }
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007550 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007551 } /* case '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007552 case '0':
7553 case '1':
7554 case '2':
7555 case '3':
7556 case '4':
7557 case '5':
7558 case '6':
7559 case '7':
7560 case '8':
7561 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02007562 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007563 if (num < 0 || num > shellparam.nparam)
7564 return -1;
7565 p = num ? shellparam.p[num - 1] : arg0;
7566 goto value;
7567 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007568 /* NB: name has form "VAR=..." */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007569 p = lookupvar(name);
7570 value:
7571 if (!p)
7572 return -1;
7573
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007574 len = strtodest(p, flags);
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007575#if ENABLE_UNICODE_SUPPORT
7576 if (subtype == VSLENGTH && len > 0) {
7577 reinit_unicode_for_ash();
7578 if (unicode_status == UNICODE_ON) {
Ron Yorston3e3bfb82016-03-18 11:29:19 +00007579 STADJUST(-len, expdest);
7580 discard = 0;
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007581 len = unicode_strlen(p);
7582 }
7583 }
7584#endif
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007585 break;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007586 }
7587
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007588 if (discard)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007589 STADJUST(-len, expdest);
Denys Vlasenko82331882020-02-24 10:02:50 +01007590
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007591 return len;
7592}
7593
7594/*
7595 * Expand a variable, and return a pointer to the next character in the
7596 * input string.
7597 */
7598static char *
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007599evalvar(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007600{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007601 char varflags;
7602 char subtype;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007603 char *var;
7604 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007605 int startloc;
7606 ssize_t varlen;
Denys Vlasenko15558952020-02-22 19:38:40 +01007607 int discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007608 int quoted;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007609
Denys Vlasenkob0d63382009-09-16 16:18:32 +02007610 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007611 subtype = varflags & VSTYPE;
Denys Vlasenko88e15702016-10-26 01:55:56 +02007612
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007613 quoted = flag & EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007614 var = p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007615 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02007616 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007617
7618 again:
Denys Vlasenko440da972018-08-05 14:29:58 +02007619 varlen = varvalue(var, varflags, flag, quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007620 if (varflags & VSNUL)
7621 varlen--;
7622
Denys Vlasenko15558952020-02-22 19:38:40 +01007623 discard = varlen < 0 ? EXP_DISCARD : 0;
7624
Denys Vlasenko82331882020-02-24 10:02:50 +01007625 switch (subtype) {
7626 case VSPLUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007627 discard ^= EXP_DISCARD;
Denys Vlasenko82331882020-02-24 10:02:50 +01007628 /* fall through */
7629 case 0:
7630 case VSMINUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007631 p = argstr(p, flag | EXP_TILDE | EXP_WORD | (discard ^ EXP_DISCARD));
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007632 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007633
Denys Vlasenko82331882020-02-24 10:02:50 +01007634 case VSASSIGN:
7635 case VSQUESTION:
Denys Vlasenko15558952020-02-22 19:38:40 +01007636 p = subevalvar(p, var, 0, startloc, varflags,
7637 (flag & ~QUOTES_ESC) | (discard ^ EXP_DISCARD));
7638
7639 if ((flag | ~discard) & EXP_DISCARD)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007640 goto record;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007641
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007642 varflags &= ~VSNUL;
Denys Vlasenko15558952020-02-22 19:38:40 +01007643 subtype = VSNORMAL;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007644 goto again;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007645 }
7646
Denys Vlasenko15558952020-02-22 19:38:40 +01007647 if ((discard & ~flag) && uflag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007648 varunset(p, var, 0, 0);
7649
7650 if (subtype == VSLENGTH) {
Denys Vlasenko82331882020-02-24 10:02:50 +01007651 p++;
7652 if (flag & EXP_DISCARD)
7653 return p;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007654 cvtnum(varlen > 0 ? varlen : 0, flag);
Denys Vlasenko15558952020-02-22 19:38:40 +01007655 goto really_record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007656 }
7657
Denys Vlasenko82331882020-02-24 10:02:50 +01007658 if (subtype == VSNORMAL)
7659 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007660
7661#if DEBUG
7662 switch (subtype) {
7663 case VSTRIMLEFT:
7664 case VSTRIMLEFTMAX:
7665 case VSTRIMRIGHT:
7666 case VSTRIMRIGHTMAX:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007667#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007668 case VSSUBSTR:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007669#endif
7670#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007671 case VSREPLACE:
7672 case VSREPLACEALL:
7673#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007674 break;
7675 default:
7676 abort();
7677 }
7678#endif
7679
Denys Vlasenko15558952020-02-22 19:38:40 +01007680 flag |= discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007681 if (!(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007682 /*
7683 * Terminate the string and start recording the pattern
7684 * right after it
7685 */
7686 STPUTC('\0', expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007687 }
7688
Denys Vlasenko82331882020-02-24 10:02:50 +01007689 patloc = expdest - (char *)stackblock();
7690 p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
Denys Vlasenko4ace3852020-02-16 18:42:50 +01007691
Denys Vlasenko82331882020-02-24 10:02:50 +01007692 record:
Denys Vlasenko15558952020-02-22 19:38:40 +01007693 if ((flag | discard) & EXP_DISCARD)
Denys Vlasenko82331882020-02-24 10:02:50 +01007694 return p;
7695
Denys Vlasenko15558952020-02-22 19:38:40 +01007696 really_record:
Denys Vlasenko82331882020-02-24 10:02:50 +01007697 if (quoted) {
7698 quoted = *var == '@' && shellparam.nparam;
7699 if (!quoted)
7700 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007701 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007702 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007703 return p;
7704}
7705
7706/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007707 * Add a file name to the list.
7708 */
7709static void
7710addfname(const char *name)
7711{
7712 struct strlist *sp;
7713
Denis Vlasenko597906c2008-02-20 16:38:54 +00007714 sp = stzalloc(sizeof(*sp));
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02007715 sp->text = sstrdup(name);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007716 *exparg.lastp = sp;
7717 exparg.lastp = &sp->next;
7718}
7719
Felix Fietkaub5b21122017-01-31 21:58:55 +01007720/* Avoid glob() (and thus, stat() et al) for words like "echo" */
7721static int
7722hasmeta(const char *p)
7723{
7724 static const char chars[] ALIGN1 = {
7725 '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
7726 };
7727
7728 for (;;) {
7729 p = strpbrk(p, chars);
7730 if (!p)
7731 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007732 switch ((unsigned char)*p) {
Felix Fietkaub5b21122017-01-31 21:58:55 +01007733 case CTLQUOTEMARK:
7734 for (;;) {
7735 p++;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007736 if ((unsigned char)*p == CTLQUOTEMARK)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007737 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007738 if ((unsigned char)*p == CTLESC)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007739 p++;
7740 if (*p == '\0') /* huh? */
7741 return 0;
7742 }
7743 break;
7744 case '\\':
7745 case CTLESC:
7746 p++;
7747 if (*p == '\0')
7748 return 0;
7749 break;
7750 case '[':
7751 if (!strchr(p + 1, ']')) {
7752 /* It's not a properly closed [] pattern,
7753 * but other metas may follow. Continue checking.
7754 * my[file* _is_ globbed by bash
7755 * and matches filenames like "my[file1".
7756 */
7757 break;
7758 }
7759 /* fallthrough */
7760 default:
7761 /* case '*': */
7762 /* case '?': */
7763 return 1;
7764 }
7765 p++;
7766 }
7767
7768 return 0;
7769}
7770
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007771/* If we want to use glob() from libc... */
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007772#if !ENABLE_ASH_INTERNAL_GLOB
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007773
7774/* Add the result of glob() to the list */
7775static void
7776addglob(const glob_t *pglob)
7777{
7778 char **p = pglob->gl_pathv;
7779
7780 do {
7781 addfname(*p);
7782 } while (*++p);
7783}
7784static void
7785expandmeta(struct strlist *str /*, int flag*/)
7786{
7787 /* TODO - EXP_REDIR */
7788
7789 while (str) {
7790 char *p;
7791 glob_t pglob;
7792 int i;
7793
7794 if (fflag)
7795 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007796
Felix Fietkaub5b21122017-01-31 21:58:55 +01007797 if (!hasmeta(str->text))
7798 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007799
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007800 INT_OFF;
7801 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007802// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
7803// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?)
7804//
7805// glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping:
7806// if you pass it "file\?", it returns "file\?", not "file?", if no match.
7807// Which means you need to unescape the string, right? Not so fast:
7808// if there _is_ a file named "file\?" (with backslash), it is returned
7809// as "file\?" too (whichever pattern you used to find it, say, "file*").
Denys Vlasenko10ad6222017-04-17 16:13:32 +02007810// You DON'T KNOW by looking at the result whether you need to unescape it.
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007811//
7812// Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?",
7813// returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file.
7814// Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match).
7815// With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly.
7816// i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob);
7817// i = glob(p, GLOB_NOMAGIC, NULL, &pglob);
7818 i = glob(p, 0, NULL, &pglob);
7819 //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-");
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007820 if (p != str->text)
7821 free(p);
7822 switch (i) {
7823 case 0:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007824#if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007825 /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */
7826 if (!(pglob.gl_flags & GLOB_MAGCHAR))
7827 goto nometa2;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007828#endif
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007829 addglob(&pglob);
7830 globfree(&pglob);
7831 INT_ON;
7832 break;
7833 case GLOB_NOMATCH:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007834 //nometa2:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007835 globfree(&pglob);
7836 INT_ON;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007837 nometa:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007838 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007839 rmescapes(str->text, 0, NULL);
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007840 exparg.lastp = &str->next;
7841 break;
7842 default: /* GLOB_NOSPACE */
7843 globfree(&pglob);
7844 INT_ON;
7845 ash_msg_and_raise_error(bb_msg_memory_exhausted);
7846 }
7847 str = str->next;
7848 }
7849}
7850
7851#else
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007852/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007853
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007854/*
7855 * Do metacharacter (i.e. *, ?, [...]) expansion.
7856 */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007857typedef struct exp_t {
7858 char *dir;
7859 unsigned dir_max;
7860} exp_t;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007861static void
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007862expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007863{
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007864#define expdir exp->dir
7865#define expdir_max exp->dir_max
7866 char *enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007867 char *p;
7868 const char *cp;
7869 char *start;
7870 char *endname;
7871 int metaflag;
7872 struct stat statb;
7873 DIR *dirp;
7874 struct dirent *dp;
7875 int atend;
7876 int matchdot;
Ron Yorstonca25af92015-09-04 10:32:41 +01007877 int esc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007878
7879 metaflag = 0;
7880 start = name;
Ron Yorstonca25af92015-09-04 10:32:41 +01007881 for (p = name; esc = 0, *p; p += esc + 1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007882 if (*p == '*' || *p == '?')
7883 metaflag = 1;
7884 else if (*p == '[') {
7885 char *q = p + 1;
7886 if (*q == '!')
7887 q++;
7888 for (;;) {
7889 if (*q == '\\')
7890 q++;
7891 if (*q == '/' || *q == '\0')
7892 break;
7893 if (*++q == ']') {
7894 metaflag = 1;
7895 break;
7896 }
7897 }
Ron Yorstonca25af92015-09-04 10:32:41 +01007898 } else {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007899 if (*p == '\\' && p[1])
Ron Yorstonca25af92015-09-04 10:32:41 +01007900 esc++;
7901 if (p[esc] == '/') {
7902 if (metaflag)
7903 break;
7904 start = p + esc + 1;
7905 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007906 }
7907 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007908 if (metaflag == 0) { /* we've reached the end of the file name */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007909 if (!expdir_len)
7910 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007911 p = name;
7912 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007913 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007914 p++;
7915 *enddir++ = *p;
7916 } while (*p++);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007917 if (lstat(expdir, &statb) == 0)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007918 addfname(expdir);
7919 return;
7920 }
7921 endname = p;
7922 if (name < start) {
7923 p = name;
7924 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007925 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007926 p++;
7927 *enddir++ = *p++;
7928 } while (p < start);
7929 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007930 *enddir = '\0';
7931 cp = expdir;
7932 expdir_len = enddir - cp;
7933 if (!expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007934 cp = ".";
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007935 dirp = opendir(cp);
7936 if (dirp == NULL)
7937 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007938 if (*endname == 0) {
7939 atend = 1;
7940 } else {
7941 atend = 0;
Ron Yorstonca25af92015-09-04 10:32:41 +01007942 *endname = '\0';
7943 endname += esc + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007944 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007945 name_len -= endname - name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007946 matchdot = 0;
7947 p = start;
7948 if (*p == '\\')
7949 p++;
7950 if (*p == '.')
7951 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007952 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007953 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007954 continue;
7955 if (pmatch(start, dp->d_name)) {
7956 if (atend) {
7957 strcpy(enddir, dp->d_name);
7958 addfname(expdir);
7959 } else {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007960 unsigned offset;
7961 unsigned len;
7962
7963 p = stpcpy(enddir, dp->d_name);
7964 *p = '/';
7965
7966 offset = p - expdir + 1;
7967 len = offset + name_len + NAME_MAX;
7968 if (len > expdir_max) {
7969 len += PATH_MAX;
7970 expdir = ckrealloc(expdir, len);
7971 expdir_max = len;
7972 }
7973
7974 expmeta(exp, endname, name_len, offset);
7975 enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007976 }
7977 }
7978 }
7979 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007980 if (!atend)
Ron Yorstonca25af92015-09-04 10:32:41 +01007981 endname[-esc - 1] = esc ? '\\' : '/';
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007982#undef expdir
7983#undef expdir_max
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007984}
7985
7986static struct strlist *
7987msort(struct strlist *list, int len)
7988{
7989 struct strlist *p, *q = NULL;
7990 struct strlist **lpp;
7991 int half;
7992 int n;
7993
7994 if (len <= 1)
7995 return list;
7996 half = len >> 1;
7997 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007998 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007999 q = p;
8000 p = p->next;
8001 }
8002 q->next = NULL; /* terminate first half of list */
8003 q = msort(list, half); /* sort first half of list */
8004 p = msort(p, len - half); /* sort second half */
8005 lpp = &list;
8006 for (;;) {
8007#if ENABLE_LOCALE_SUPPORT
8008 if (strcoll(p->text, q->text) < 0)
8009#else
8010 if (strcmp(p->text, q->text) < 0)
8011#endif
8012 {
8013 *lpp = p;
8014 lpp = &p->next;
8015 p = *lpp;
8016 if (p == NULL) {
8017 *lpp = q;
8018 break;
8019 }
8020 } else {
8021 *lpp = q;
8022 lpp = &q->next;
8023 q = *lpp;
8024 if (q == NULL) {
8025 *lpp = p;
8026 break;
8027 }
8028 }
8029 }
8030 return list;
8031}
8032
8033/*
8034 * Sort the results of file name expansion. It calculates the number of
8035 * strings to sort and then calls msort (short for merge sort) to do the
8036 * work.
8037 */
8038static struct strlist *
8039expsort(struct strlist *str)
8040{
8041 int len;
8042 struct strlist *sp;
8043
8044 len = 0;
8045 for (sp = str; sp; sp = sp->next)
8046 len++;
8047 return msort(str, len);
8048}
8049
8050static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00008051expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008052{
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008053 /* TODO - EXP_REDIR */
8054
8055 while (str) {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008056 exp_t exp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008057 struct strlist **savelastp;
8058 struct strlist *sp;
8059 char *p;
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008060 unsigned len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008061
8062 if (fflag)
8063 goto nometa;
Felix Fietkaub5b21122017-01-31 21:58:55 +01008064 if (!hasmeta(str->text))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008065 goto nometa;
8066 savelastp = exparg.lastp;
8067
8068 INT_OFF;
Ron Yorston549deab2015-05-18 09:57:51 +02008069 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008070 len = strlen(p);
8071 exp.dir_max = len + PATH_MAX;
8072 exp.dir = ckmalloc(exp.dir_max);
8073
8074 expmeta(&exp, p, len, 0);
8075 free(exp.dir);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008076 if (p != str->text)
8077 free(p);
8078 INT_ON;
8079 if (exparg.lastp == savelastp) {
8080 /*
8081 * no matches
8082 */
8083 nometa:
8084 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01008085 rmescapes(str->text, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008086 exparg.lastp = &str->next;
8087 } else {
8088 *exparg.lastp = NULL;
8089 *savelastp = sp = expsort(*savelastp);
8090 while (sp->next != NULL)
8091 sp = sp->next;
8092 exparg.lastp = &sp->next;
8093 }
8094 str = str->next;
8095 }
8096}
Denys Vlasenko514b51d2016-10-01 14:33:08 +02008097#endif /* ENABLE_ASH_INTERNAL_GLOB */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008098
8099/*
8100 * Perform variable substitution and command substitution on an argument,
8101 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
8102 * perform splitting and file name expansion. When arglist is NULL, perform
8103 * here document expansion.
8104 */
8105static void
8106expandarg(union node *arg, struct arglist *arglist, int flag)
8107{
8108 struct strlist *sp;
8109 char *p;
8110
8111 argbackq = arg->narg.backquote;
8112 STARTSTACKSTR(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008113 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008114 argstr(arg->narg.text, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008115 if (arglist == NULL) {
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008116 /* here document expanded */
8117 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008118 }
Denys Vlasenko82331882020-02-24 10:02:50 +01008119 p = grabstackstr(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008120 TRACE(("expandarg: p:'%s'\n", p));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008121 exparg.lastp = &exparg.list;
8122 /*
8123 * TODO - EXP_REDIR
8124 */
8125 if (flag & EXP_FULL) {
8126 ifsbreakup(p, &exparg);
8127 *exparg.lastp = NULL;
8128 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008129 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008130 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +00008131 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008132 sp->text = p;
8133 *exparg.lastp = sp;
8134 exparg.lastp = &sp->next;
8135 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008136 *exparg.lastp = NULL;
8137 if (exparg.list) {
8138 *arglist->lastp = exparg.list;
8139 arglist->lastp = exparg.lastp;
8140 }
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008141
8142 out:
8143 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008144}
8145
8146/*
8147 * Expand shell variables and backquotes inside a here document.
8148 */
8149static void
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01008150expandhere(union node *arg)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008151{
Ron Yorston549deab2015-05-18 09:57:51 +02008152 expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008153}
8154
8155/*
8156 * Returns true if the pattern matches the string.
8157 */
8158static int
8159patmatch(char *pattern, const char *string)
8160{
Denys Vlasenkobd43c672017-07-05 23:12:15 +02008161 char *p = preglob(pattern, 0);
Denys Vlasenko4476c702017-08-15 15:27:41 +02008162 int r = pmatch(p, string);
8163 //bb_error_msg("!fnmatch(pattern:'%s',str:'%s',0):%d", p, string, r);
8164 return r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008165}
8166
8167/*
8168 * See if a pattern matches in a case statement.
8169 */
8170static int
8171casematch(union node *pattern, char *val)
8172{
8173 struct stackmark smark;
8174 int result;
8175
8176 setstackmark(&smark);
8177 argbackq = pattern->narg.backquote;
8178 STARTSTACKSTR(expdest);
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008179 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008180 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008181 result = patmatch(stackblock(), val);
8182 popstackmark(&smark);
8183 return result;
8184}
8185
8186
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008187/* ============ find_command */
8188
8189struct builtincmd {
8190 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008191 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008192 /* unsigned flags; */
8193};
8194#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00008195/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008196 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008197#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008198#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008199
8200struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008201 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008202 union param {
8203 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008204 /* index >= 0 for commands without path (slashes) */
8205 /* (TODO: what exactly does the value mean? PATH position?) */
8206 /* index == -1 for commands with slashes */
8207 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008208 const struct builtincmd *cmd;
8209 struct funcnode *func;
8210 } u;
8211};
8212/* values of cmdtype */
8213#define CMDUNKNOWN -1 /* no entry in table for command */
8214#define CMDNORMAL 0 /* command is an executable program */
8215#define CMDFUNCTION 1 /* command is a shell function */
8216#define CMDBUILTIN 2 /* command is a shell builtin */
8217
8218/* action to find_command() */
8219#define DO_ERR 0x01 /* prints errors */
8220#define DO_ABS 0x02 /* checks absolute paths */
8221#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
8222#define DO_ALTPATH 0x08 /* using alternate path */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008223#define DO_REGBLTIN 0x10 /* regular built-ins and functions only */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008224
8225static void find_command(char *, struct cmdentry *, int, const char *);
8226
8227
8228/* ============ Hashing commands */
8229
8230/*
8231 * When commands are first encountered, they are entered in a hash table.
8232 * This ensures that a full path search will not have to be done for them
8233 * on each invocation.
8234 *
8235 * We should investigate converting to a linear search, even though that
8236 * would make the command name "hash" a misnomer.
8237 */
8238
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008239struct tblentry {
8240 struct tblentry *next; /* next entry in hash chain */
8241 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008242 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008243 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008244 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008245};
8246
Denis Vlasenko01631112007-12-16 17:20:38 +00008247static struct tblentry **cmdtable;
8248#define INIT_G_cmdtable() do { \
8249 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
8250} while (0)
8251
8252static int builtinloc = -1; /* index in path of %builtin, or -1 */
8253
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008254
8255static void
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008256tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008257{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008258#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008259 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00008260 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02008261 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00008262 while (*envp)
8263 putenv(*envp++);
Denys Vlasenko035486c2017-07-31 04:09:19 +02008264 popredir(/*drop:*/ 1);
Denys Vlasenko80e8e3c2017-08-07 19:24:57 +02008265 run_noexec_applet_and_exit(applet_no, cmd, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00008266 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008267 /* re-exec ourselves with the new arguments */
8268 execve(bb_busybox_exec_path, argv, envp);
8269 /* If they called chroot or otherwise made the binary no longer
8270 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008271 }
8272#endif
8273
8274 repeat:
8275#ifdef SYSV
8276 do {
8277 execve(cmd, argv, envp);
8278 } while (errno == EINTR);
8279#else
8280 execve(cmd, argv, envp);
8281#endif
Ron Yorstonca82b532018-11-01 11:45:03 +01008282
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008283 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008284 /* Run "cmd" as a shell script:
8285 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
8286 * "If the execve() function fails with ENOEXEC, the shell
8287 * shall execute a command equivalent to having a shell invoked
8288 * with the command name as its first operand,
8289 * with any remaining arguments passed to the new shell"
8290 *
8291 * That is, do not use $SHELL, user's shell, or /bin/sh;
8292 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01008293 *
8294 * Note that bash reads ~80 chars of the file, and if it sees
8295 * a zero byte before it sees newline, it doesn't try to
8296 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01008297 * message and exit code 126. For one, this prevents attempts
8298 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008299 */
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008300 argv[0] = (char*) cmd;
8301 cmd = bb_busybox_exec_path;
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008302 /* NB: this is only possible because all callers of shellexec()
8303 * ensure that the argv[-1] slot exists!
8304 */
8305 argv--;
8306 argv[0] = (char*) "ash";
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008307 goto repeat;
8308 }
8309}
8310
8311/*
8312 * Exec a program. Never returns. If you change this routine, you may
8313 * have to change the find_command routine as well.
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008314 * argv[-1] must exist and be writable! See tryexec() for why.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008315 */
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008316static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8317static void shellexec(char *prog, char **argv, const char *path, int idx)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008318{
8319 char *cmdname;
8320 int e;
8321 char **envp;
8322 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008323 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008324
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01008325 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008326 if (strchr(prog, '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008327#if ENABLE_FEATURE_SH_STANDALONE
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008328 || (applet_no = find_applet_by_name(prog)) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008329#endif
8330 ) {
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008331 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008332 if (applet_no >= 0) {
8333 /* We tried execing ourself, but it didn't work.
8334 * Maybe /proc/self/exe doesn't exist?
8335 * Try $PATH search.
8336 */
8337 goto try_PATH;
8338 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008339 e = errno;
8340 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008341 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008342 e = ENOENT;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008343 while (padvance(&path, argv[0]) >= 0) {
8344 cmdname = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008345 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00008346 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008347 if (errno != ENOENT && errno != ENOTDIR)
8348 e = errno;
8349 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008350 }
8351 }
8352
8353 /* Map to POSIX errors */
8354 switch (e) {
Denys Vlasenko2596f412018-08-05 18:04:09 +02008355 default:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008356 exerrno = 126;
8357 break;
Denys Vlasenko2596f412018-08-05 18:04:09 +02008358 case ELOOP:
8359 case ENAMETOOLONG:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008360 case ENOENT:
Denys Vlasenko2596f412018-08-05 18:04:09 +02008361 case ENOTDIR:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008362 exerrno = 127;
8363 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008364 }
8365 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008366 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008367 prog, e, suppress_int));
Denys Vlasenkof977e002020-02-20 16:54:29 +01008368 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008369 /* NOTREACHED */
8370}
8371
8372static void
8373printentry(struct tblentry *cmdp)
8374{
8375 int idx;
8376 const char *path;
8377 char *name;
8378
8379 idx = cmdp->param.index;
8380 path = pathval();
8381 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008382 padvance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008383 } while (--idx >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008384 name = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008385 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8386}
8387
8388/*
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008389 * Clear out command entries.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008390 */
8391static void
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008392clearcmdentry(void)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008393{
8394 struct tblentry **tblp;
8395 struct tblentry **pp;
8396 struct tblentry *cmdp;
8397
8398 INT_OFF;
8399 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
8400 pp = tblp;
8401 while ((cmdp = *pp) != NULL) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008402 if (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008403 || (cmdp->cmdtype == CMDBUILTIN
8404 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8405 && builtinloc > 0
8406 )
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008407 ) {
8408 *pp = cmdp->next;
8409 free(cmdp);
8410 } else {
8411 pp = &cmdp->next;
8412 }
8413 }
8414 }
8415 INT_ON;
8416}
8417
8418/*
8419 * Locate a command in the command hash table. If "add" is nonzero,
8420 * add the command to the table if it is not already present. The
8421 * variable "lastcmdentry" is set to point to the address of the link
8422 * pointing to the entry, so that delete_cmd_entry can delete the
8423 * entry.
8424 *
8425 * Interrupts must be off if called with add != 0.
8426 */
8427static struct tblentry **lastcmdentry;
8428
8429static struct tblentry *
8430cmdlookup(const char *name, int add)
8431{
8432 unsigned int hashval;
8433 const char *p;
8434 struct tblentry *cmdp;
8435 struct tblentry **pp;
8436
8437 p = name;
8438 hashval = (unsigned char)*p << 4;
8439 while (*p)
8440 hashval += (unsigned char)*p++;
8441 hashval &= 0x7FFF;
8442 pp = &cmdtable[hashval % CMDTABLESIZE];
8443 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8444 if (strcmp(cmdp->cmdname, name) == 0)
8445 break;
8446 pp = &cmdp->next;
8447 }
8448 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008449 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
8450 + strlen(name)
8451 /* + 1 - already done because
8452 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00008453 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008454 cmdp->cmdtype = CMDUNKNOWN;
8455 strcpy(cmdp->cmdname, name);
8456 }
8457 lastcmdentry = pp;
8458 return cmdp;
8459}
8460
8461/*
8462 * Delete the command entry returned on the last lookup.
8463 */
8464static void
8465delete_cmd_entry(void)
8466{
8467 struct tblentry *cmdp;
8468
8469 INT_OFF;
8470 cmdp = *lastcmdentry;
8471 *lastcmdentry = cmdp->next;
8472 if (cmdp->cmdtype == CMDFUNCTION)
8473 freefunc(cmdp->param.func);
8474 free(cmdp);
8475 INT_ON;
8476}
8477
8478/*
8479 * Add a new command entry, replacing any existing command entry for
8480 * the same name - except special builtins.
8481 */
8482static void
8483addcmdentry(char *name, struct cmdentry *entry)
8484{
8485 struct tblentry *cmdp;
8486
8487 cmdp = cmdlookup(name, 1);
8488 if (cmdp->cmdtype == CMDFUNCTION) {
8489 freefunc(cmdp->param.func);
8490 }
8491 cmdp->cmdtype = entry->cmdtype;
8492 cmdp->param = entry->u;
8493 cmdp->rehash = 0;
8494}
8495
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008496static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008497hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008498{
8499 struct tblentry **pp;
8500 struct tblentry *cmdp;
8501 int c;
8502 struct cmdentry entry;
8503 char *name;
8504
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008505 if (nextopt("r") != '\0') {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008506 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008507 return 0;
8508 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008509
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008510 if (*argptr == NULL) {
8511 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8512 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8513 if (cmdp->cmdtype == CMDNORMAL)
8514 printentry(cmdp);
8515 }
8516 }
8517 return 0;
8518 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008519
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008520 c = 0;
8521 while ((name = *argptr) != NULL) {
8522 cmdp = cmdlookup(name, 0);
8523 if (cmdp != NULL
8524 && (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008525 || (cmdp->cmdtype == CMDBUILTIN
8526 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8527 && builtinloc > 0
8528 )
8529 )
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008530 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008531 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008532 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008533 find_command(name, &entry, DO_ERR, pathval());
8534 if (entry.cmdtype == CMDUNKNOWN)
8535 c = 1;
8536 argptr++;
8537 }
8538 return c;
8539}
8540
8541/*
8542 * Called when a cd is done. Marks all commands so the next time they
8543 * are executed they will be rehashed.
8544 */
8545static void
8546hashcd(void)
8547{
8548 struct tblentry **pp;
8549 struct tblentry *cmdp;
8550
8551 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8552 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008553 if (cmdp->cmdtype == CMDNORMAL
8554 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02008555 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008556 && builtinloc > 0)
8557 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008558 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008559 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008560 }
8561 }
8562}
8563
8564/*
8565 * Fix command hash table when PATH changed.
8566 * Called before PATH is changed. The argument is the new value of PATH;
8567 * pathval() still returns the old value at this point.
8568 * Called with interrupts off.
8569 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008570static void FAST_FUNC
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008571changepath(const char *newval)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008572{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008573 const char *new;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008574 int idx;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008575 int bltin;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008576
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008577 new = newval;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008578 idx = 0;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008579 bltin = -1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008580 for (;;) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008581 if (*new == '%' && prefix(new + 1, "builtin")) {
8582 bltin = idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008583 break;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008584 }
8585 new = strchr(new, ':');
8586 if (!new)
8587 break;
8588 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02008589 new++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008590 }
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008591 builtinloc = bltin;
8592 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008593}
Ron Yorston95ebcf72015-11-03 09:42:23 +00008594enum {
8595 TEOF,
8596 TNL,
8597 TREDIR,
8598 TWORD,
8599 TSEMI,
8600 TBACKGND,
8601 TAND,
8602 TOR,
8603 TPIPE,
8604 TLP,
8605 TRP,
8606 TENDCASE,
8607 TENDBQUOTE,
8608 TNOT,
8609 TCASE,
8610 TDO,
8611 TDONE,
8612 TELIF,
8613 TELSE,
8614 TESAC,
8615 TFI,
8616 TFOR,
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008617#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +00008618 TFUNCTION,
8619#endif
8620 TIF,
8621 TIN,
8622 TTHEN,
8623 TUNTIL,
8624 TWHILE,
8625 TBEGIN,
8626 TEND
8627};
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008628typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008629
Denys Vlasenko888527c2016-10-02 16:54:17 +02008630/* Nth bit indicates if token marks the end of a list */
8631enum {
8632 tokendlist = 0
8633 /* 0 */ | (1u << TEOF)
8634 /* 1 */ | (0u << TNL)
8635 /* 2 */ | (0u << TREDIR)
8636 /* 3 */ | (0u << TWORD)
8637 /* 4 */ | (0u << TSEMI)
8638 /* 5 */ | (0u << TBACKGND)
8639 /* 6 */ | (0u << TAND)
8640 /* 7 */ | (0u << TOR)
8641 /* 8 */ | (0u << TPIPE)
8642 /* 9 */ | (0u << TLP)
8643 /* 10 */ | (1u << TRP)
8644 /* 11 */ | (1u << TENDCASE)
8645 /* 12 */ | (1u << TENDBQUOTE)
8646 /* 13 */ | (0u << TNOT)
8647 /* 14 */ | (0u << TCASE)
8648 /* 15 */ | (1u << TDO)
8649 /* 16 */ | (1u << TDONE)
8650 /* 17 */ | (1u << TELIF)
8651 /* 18 */ | (1u << TELSE)
8652 /* 19 */ | (1u << TESAC)
8653 /* 20 */ | (1u << TFI)
8654 /* 21 */ | (0u << TFOR)
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008655#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008656 /* 22 */ | (0u << TFUNCTION)
Denys Vlasenko80729a42016-10-02 22:33:15 +02008657#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008658 /* 23 */ | (0u << TIF)
8659 /* 24 */ | (0u << TIN)
8660 /* 25 */ | (1u << TTHEN)
8661 /* 26 */ | (0u << TUNTIL)
8662 /* 27 */ | (0u << TWHILE)
8663 /* 28 */ | (0u << TBEGIN)
8664 /* 29 */ | (1u << TEND)
8665 , /* thus far 29 bits used */
8666};
8667
Denys Vlasenko965b7952020-11-30 13:03:03 +01008668static const char *const tokname_array[] ALIGN_PTR = {
Denys Vlasenko888527c2016-10-02 16:54:17 +02008669 "end of file",
8670 "newline",
8671 "redirection",
8672 "word",
8673 ";",
8674 "&",
8675 "&&",
8676 "||",
8677 "|",
8678 "(",
8679 ")",
8680 ";;",
8681 "`",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008682#define KWDOFFSET 13
8683 /* the following are keywords */
Denys Vlasenko888527c2016-10-02 16:54:17 +02008684 "!",
8685 "case",
8686 "do",
8687 "done",
8688 "elif",
8689 "else",
8690 "esac",
8691 "fi",
8692 "for",
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008693#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008694 "function",
Ron Yorston95ebcf72015-11-03 09:42:23 +00008695#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008696 "if",
8697 "in",
8698 "then",
8699 "until",
8700 "while",
8701 "{",
8702 "}",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008703};
8704
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008705/* Wrapper around strcmp for qsort/bsearch/... */
8706static int
8707pstrcmp(const void *a, const void *b)
8708{
Denys Vlasenko888527c2016-10-02 16:54:17 +02008709 return strcmp((char*)a, *(char**)b);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008710}
8711
8712static const char *const *
8713findkwd(const char *s)
8714{
8715 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008716 ARRAY_SIZE(tokname_array) - KWDOFFSET,
8717 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008718}
8719
8720/*
8721 * Locate and print what a word is...
8722 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008723static int
Ron Yorston3f221112015-08-03 13:47:33 +01008724describe_command(char *command, const char *path, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008725{
8726 struct cmdentry entry;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008727#if ENABLE_ASH_ALIAS
8728 const struct alias *ap;
8729#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008730
8731 path = path ? path : pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008732
8733 if (describe_command_verbose) {
8734 out1str(command);
8735 }
8736
8737 /* First look at the keywords */
8738 if (findkwd(command)) {
8739 out1str(describe_command_verbose ? " is a shell keyword" : command);
8740 goto out;
8741 }
8742
8743#if ENABLE_ASH_ALIAS
8744 /* Then look at the aliases */
8745 ap = lookupalias(command, 0);
8746 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008747 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008748 out1str("alias ");
8749 printalias(ap);
8750 return 0;
8751 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00008752 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008753 goto out;
8754 }
8755#endif
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008756 /* Brute force */
8757 find_command(command, &entry, DO_ABS, path);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008758
8759 switch (entry.cmdtype) {
8760 case CMDNORMAL: {
8761 int j = entry.u.index;
8762 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008763 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008764 p = command;
8765 } else {
8766 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008767 padvance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008768 } while (--j >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008769 p = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008770 }
8771 if (describe_command_verbose) {
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008772 out1fmt(" is %s", p);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008773 } else {
8774 out1str(p);
8775 }
8776 break;
8777 }
8778
8779 case CMDFUNCTION:
8780 if (describe_command_verbose) {
Denys Vlasenko63c42af2018-07-24 17:08:04 +02008781 /*out1str(" is a shell function");*/
8782 out1str(" is a function"); /* bash says this */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008783 } else {
8784 out1str(command);
8785 }
8786 break;
8787
8788 case CMDBUILTIN:
8789 if (describe_command_verbose) {
8790 out1fmt(" is a %sshell builtin",
8791 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
8792 "special " : nullstr
8793 );
8794 } else {
8795 out1str(command);
8796 }
8797 break;
8798
8799 default:
8800 if (describe_command_verbose) {
8801 out1str(": not found\n");
8802 }
8803 return 127;
8804 }
8805 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01008806 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008807 return 0;
8808}
8809
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008810static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008811typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008812{
Denis Vlasenko46846e22007-05-20 13:08:31 +00008813 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008814 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00008815 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008816
Denis Vlasenko46846e22007-05-20 13:08:31 +00008817 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00008818 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008819 i++;
8820 verbose = 0;
8821 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00008822 while (argv[i]) {
Ron Yorston3f221112015-08-03 13:47:33 +01008823 err |= describe_command(argv[i++], NULL, verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008824 }
8825 return err;
8826}
8827
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008828static struct strlist *
8829fill_arglist(struct arglist *arglist, union node **argpp)
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008830{
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008831 struct strlist **lastp = arglist->lastp;
8832 union node *argp;
8833
8834 while ((argp = *argpp) != NULL) {
8835 expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
8836 *argpp = argp->narg.next;
8837 if (*lastp)
8838 break;
8839 }
8840
8841 return *lastp;
8842}
8843
Ron Yorstonda7a6db2020-02-27 09:50:18 +00008844#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008845/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
8846static int
8847parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
8848{
8849 struct strlist *sp = arglist->list;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008850 char *cp, c;
8851
8852 for (;;) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008853 sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
8854 if (!sp)
8855 return 0;
8856 cp = sp->text;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008857 if (*cp++ != '-')
8858 break;
8859 c = *cp++;
8860 if (!c)
8861 break;
8862 if (c == '-' && !*cp) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008863 if (!sp->next && !fill_arglist(arglist, argpp))
8864 return 0;
8865 sp = sp->next;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008866 break;
8867 }
8868 do {
8869 switch (c) {
8870 case 'p':
8871 *path = bb_default_path;
8872 break;
8873 default:
8874 /* run 'typecmd' for other options */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008875 return 0;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008876 }
8877 c = *cp++;
8878 } while (c);
8879 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008880
8881 arglist->list = sp;
8882 return DO_NOFUNC;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008883}
8884
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008885static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008886commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008887{
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008888 char *cmd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008889 int c;
8890 enum {
8891 VERIFY_BRIEF = 1,
8892 VERIFY_VERBOSE = 2,
8893 } verify = 0;
Ron Yorston3f221112015-08-03 13:47:33 +01008894 const char *path = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008895
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008896 /* "command [-p] PROG ARGS" (that is, without -V or -v)
8897 * never reaches this function.
8898 */
8899
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008900 while ((c = nextopt("pvV")) != '\0')
8901 if (c == 'V')
8902 verify |= VERIFY_VERBOSE;
8903 else if (c == 'v')
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008904 /*verify |= VERIFY_BRIEF*/;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008905#if DEBUG
8906 else if (c != 'p')
8907 abort();
8908#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008909 else
8910 path = bb_default_path;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008911
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00008912 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008913 cmd = *argptr;
8914 if (/*verify && */ cmd)
8915 return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008916
8917 return 0;
8918}
8919#endif
8920
8921
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008922/*static int funcblocksize; // size of structures in function */
8923/*static int funcstringsize; // size of strings in node */
Denis Vlasenko340299a2008-11-21 10:36:36 +00008924static void *funcblock; /* block to allocate function from */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008925static char *funcstring_end; /* end of block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008926
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02008927static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00008928 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8929 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
8930 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
8931 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8932 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8933 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8934 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8935 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8936 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8937 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8938 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8939 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8940 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8941 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8942 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8943 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8944 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008945#if BASH_REDIR_OUTPUT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008946 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008947#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008948 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8949 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8950 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8951 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8952 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8953 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8954 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8955 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8956 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008957};
8958
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008959static int calcsize(int funcblocksize, union node *n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008960
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008961static int
8962sizenodelist(int funcblocksize, struct nodelist *lp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008963{
8964 while (lp) {
8965 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008966 funcblocksize = calcsize(funcblocksize, lp->n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008967 lp = lp->next;
8968 }
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008969 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008970}
8971
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008972static int
8973calcsize(int funcblocksize, union node *n)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008974{
8975 if (n == NULL)
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008976 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008977 funcblocksize += nodesize[n->type];
8978 switch (n->type) {
8979 case NCMD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008980 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8981 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8982 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008983 break;
8984 case NPIPE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008985 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008986 break;
8987 case NREDIR:
8988 case NBACKGND:
8989 case NSUBSHELL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008990 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8991 funcblocksize = calcsize(funcblocksize, n->nredir.n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008992 break;
8993 case NAND:
8994 case NOR:
8995 case NSEMI:
8996 case NWHILE:
8997 case NUNTIL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008998 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8999 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009000 break;
9001 case NIF:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009002 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
9003 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
9004 funcblocksize = calcsize(funcblocksize, n->nif.test);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009005 break;
9006 case NFOR:
Denys Vlasenko561639a2016-10-07 04:28:33 +02009007 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009008 funcblocksize = calcsize(funcblocksize, n->nfor.body);
9009 funcblocksize = calcsize(funcblocksize, n->nfor.args);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009010 break;
9011 case NCASE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009012 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
9013 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009014 break;
9015 case NCLIST:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009016 funcblocksize = calcsize(funcblocksize, n->nclist.body);
9017 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
9018 funcblocksize = calcsize(funcblocksize, n->nclist.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009019 break;
9020 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009021 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
9022 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
9023 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009024 case NARG:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009025 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
Denys Vlasenko561639a2016-10-07 04:28:33 +02009026 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009027 funcblocksize = calcsize(funcblocksize, n->narg.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009028 break;
9029 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009030#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009031 case NTO2:
9032#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009033 case NCLOBBER:
9034 case NFROM:
9035 case NFROMTO:
9036 case NAPPEND:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009037 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
9038 funcblocksize = calcsize(funcblocksize, n->nfile.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009039 break;
9040 case NTOFD:
9041 case NFROMFD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009042 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
9043 funcblocksize = calcsize(funcblocksize, n->ndup.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009044 break;
9045 case NHERE:
9046 case NXHERE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009047 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
9048 funcblocksize = calcsize(funcblocksize, n->nhere.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009049 break;
9050 case NNOT:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009051 funcblocksize = calcsize(funcblocksize, n->nnot.com);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009052 break;
9053 };
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009054 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009055}
9056
9057static char *
9058nodeckstrdup(char *s)
9059{
Denys Vlasenko561639a2016-10-07 04:28:33 +02009060 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009061 return strcpy(funcstring_end, s);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009062}
9063
9064static union node *copynode(union node *);
9065
9066static struct nodelist *
9067copynodelist(struct nodelist *lp)
9068{
9069 struct nodelist *start;
9070 struct nodelist **lpp;
9071
9072 lpp = &start;
9073 while (lp) {
9074 *lpp = funcblock;
9075 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9076 (*lpp)->n = copynode(lp->n);
9077 lp = lp->next;
9078 lpp = &(*lpp)->next;
9079 }
9080 *lpp = NULL;
9081 return start;
9082}
9083
9084static union node *
9085copynode(union node *n)
9086{
9087 union node *new;
9088
9089 if (n == NULL)
9090 return NULL;
9091 new = funcblock;
9092 funcblock = (char *) funcblock + nodesize[n->type];
9093
9094 switch (n->type) {
9095 case NCMD:
9096 new->ncmd.redirect = copynode(n->ncmd.redirect);
9097 new->ncmd.args = copynode(n->ncmd.args);
9098 new->ncmd.assign = copynode(n->ncmd.assign);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009099 new->ncmd.linno = n->ncmd.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009100 break;
9101 case NPIPE:
9102 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009103 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009104 break;
9105 case NREDIR:
9106 case NBACKGND:
9107 case NSUBSHELL:
9108 new->nredir.redirect = copynode(n->nredir.redirect);
9109 new->nredir.n = copynode(n->nredir.n);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009110 new->nredir.linno = n->nredir.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009111 break;
9112 case NAND:
9113 case NOR:
9114 case NSEMI:
9115 case NWHILE:
9116 case NUNTIL:
9117 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9118 new->nbinary.ch1 = copynode(n->nbinary.ch1);
9119 break;
9120 case NIF:
9121 new->nif.elsepart = copynode(n->nif.elsepart);
9122 new->nif.ifpart = copynode(n->nif.ifpart);
9123 new->nif.test = copynode(n->nif.test);
9124 break;
9125 case NFOR:
9126 new->nfor.var = nodeckstrdup(n->nfor.var);
9127 new->nfor.body = copynode(n->nfor.body);
9128 new->nfor.args = copynode(n->nfor.args);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009129 new->nfor.linno = n->nfor.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009130 break;
9131 case NCASE:
9132 new->ncase.cases = copynode(n->ncase.cases);
9133 new->ncase.expr = copynode(n->ncase.expr);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009134 new->ncase.linno = n->ncase.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009135 break;
9136 case NCLIST:
9137 new->nclist.body = copynode(n->nclist.body);
9138 new->nclist.pattern = copynode(n->nclist.pattern);
9139 new->nclist.next = copynode(n->nclist.next);
9140 break;
9141 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009142 new->ndefun.body = copynode(n->ndefun.body);
9143 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9144 new->ndefun.linno = n->ndefun.linno;
9145 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009146 case NARG:
9147 new->narg.backquote = copynodelist(n->narg.backquote);
9148 new->narg.text = nodeckstrdup(n->narg.text);
9149 new->narg.next = copynode(n->narg.next);
9150 break;
9151 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009152#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009153 case NTO2:
9154#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009155 case NCLOBBER:
9156 case NFROM:
9157 case NFROMTO:
9158 case NAPPEND:
9159 new->nfile.fname = copynode(n->nfile.fname);
9160 new->nfile.fd = n->nfile.fd;
9161 new->nfile.next = copynode(n->nfile.next);
9162 break;
9163 case NTOFD:
9164 case NFROMFD:
9165 new->ndup.vname = copynode(n->ndup.vname);
9166 new->ndup.dupfd = n->ndup.dupfd;
9167 new->ndup.fd = n->ndup.fd;
9168 new->ndup.next = copynode(n->ndup.next);
9169 break;
9170 case NHERE:
9171 case NXHERE:
9172 new->nhere.doc = copynode(n->nhere.doc);
9173 new->nhere.fd = n->nhere.fd;
9174 new->nhere.next = copynode(n->nhere.next);
9175 break;
9176 case NNOT:
9177 new->nnot.com = copynode(n->nnot.com);
9178 break;
9179 };
9180 new->type = n->type;
9181 return new;
9182}
9183
9184/*
9185 * Make a copy of a parse tree.
9186 */
9187static struct funcnode *
9188copyfunc(union node *n)
9189{
9190 struct funcnode *f;
9191 size_t blocksize;
9192
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009193 /*funcstringsize = 0;*/
9194 blocksize = offsetof(struct funcnode, n) + calcsize(0, n);
9195 f = ckzalloc(blocksize /* + funcstringsize */);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009196 funcblock = (char *) f + offsetof(struct funcnode, n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009197 funcstring_end = (char *) f + blocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009198 copynode(n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009199 /* f->count = 0; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009200 return f;
9201}
9202
9203/*
9204 * Define a shell function.
9205 */
9206static void
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009207defun(union node *func)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009208{
9209 struct cmdentry entry;
9210
9211 INT_OFF;
9212 entry.cmdtype = CMDFUNCTION;
9213 entry.u.func = copyfunc(func);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009214 addcmdentry(func->ndefun.text, &entry);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009215 INT_ON;
9216}
9217
Denis Vlasenko4b875702009-03-19 13:30:04 +00009218/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009219#define SKIPBREAK (1 << 0)
9220#define SKIPCONT (1 << 1)
9221#define SKIPFUNC (1 << 2)
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009222#define SKIPFUNCDEF (1 << 3)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009223static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009224static int skipcount; /* number of levels to skip */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009225static int loopnest; /* current loop nesting level */
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009226static int funcline; /* starting line number of current function, or 0 if not in a function */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009227
Denis Vlasenko4b875702009-03-19 13:30:04 +00009228/* Forward decl way out to parsing code - dotrap needs it */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009229static int evalstring(char *s, int flags);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009230
Denis Vlasenko4b875702009-03-19 13:30:04 +00009231/* Called to execute a trap.
9232 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02009233 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00009234 *
9235 * Perhaps we should avoid entering new trap handlers
9236 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009237 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009238static void
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009239dotrap(void)
9240{
Denis Vlasenko4b875702009-03-19 13:30:04 +00009241 uint8_t *g;
9242 int sig;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009243 int status, last_status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009244
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009245 if (!pending_sig)
9246 return;
9247
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009248 status = savestatus;
9249 last_status = status;
9250 if (status < 0) {
9251 status = exitstatus;
9252 savestatus = status;
9253 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009254 pending_sig = 0;
Denys Vlasenkode892052016-10-02 01:49:13 +02009255 barrier();
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009256
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009257 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009258 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009259 char *p;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009260
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009261 if (!*g)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009262 continue;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009263
9264 if (evalskip) {
9265 pending_sig = sig;
9266 break;
9267 }
9268
9269 p = trap[sig];
Denis Vlasenko4b875702009-03-19 13:30:04 +00009270 /* non-trapped SIGINT is handled separately by raise_interrupt,
9271 * don't upset it by resetting gotsig[SIGINT-1] */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009272 if (sig == SIGINT && !p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009273 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009274
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009275 TRACE(("sig %d is active, will run handler '%s'\n", sig, p));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009276 *g = 0;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009277 if (!p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009278 continue;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009279 trap_depth++;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009280 evalstring(p, 0);
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009281 trap_depth--;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009282 if (evalskip != SKIPFUNC)
9283 exitstatus = status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009284 }
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009285
9286 savestatus = last_status;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009287 TRACE(("dotrap returns\n"));
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009288}
9289
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009290/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009291static int evalloop(union node *, int);
9292static int evalfor(union node *, int);
9293static int evalcase(union node *, int);
9294static int evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009295static void expredir(union node *);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009296static int evalpipe(union node *, int);
9297static int evalcommand(union node *, int);
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009298static int evalbltin(const struct builtincmd *, int, char **, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009299static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009300
Eric Andersen62483552001-07-10 06:09:16 +00009301/*
Eric Andersenc470f442003-07-28 09:56:35 +00009302 * Evaluate a parse tree. The value is left in the global variable
9303 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00009304 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009305static int
Eric Andersenc470f442003-07-28 09:56:35 +00009306evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00009307{
Eric Andersenc470f442003-07-28 09:56:35 +00009308 int checkexit = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009309 int (*evalfn)(union node *, int);
Ron Yorstonf55161a2019-02-25 08:29:38 +00009310 struct stackmark smark;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009311 int status = 0;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009312
Ron Yorstonf55161a2019-02-25 08:29:38 +00009313 setstackmark(&smark);
9314
Denys Vlasenko41beb532021-09-07 01:52:21 +02009315 if (nflag)
9316 goto out;
9317
Eric Andersenc470f442003-07-28 09:56:35 +00009318 if (n == NULL) {
9319 TRACE(("evaltree(NULL) called\n"));
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02009320 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00009321 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009322 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009323
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009324 dotrap();
9325
Eric Andersenc470f442003-07-28 09:56:35 +00009326 switch (n->type) {
9327 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009328#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00009329 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009330 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00009331 break;
9332#endif
9333 case NNOT:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009334 status = !evaltree(n->nnot.com, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009335 goto setstatus;
9336 case NREDIR:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009337 errlinno = lineno = n->nredir.linno;
Eric Andersenc470f442003-07-28 09:56:35 +00009338 expredir(n->nredir.redirect);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02009339 pushredir(n->nredir.redirect);
Eric Andersenc470f442003-07-28 09:56:35 +00009340 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
9341 if (!status) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009342 status = evaltree(n->nredir.n, flags & EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009343 }
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02009344 if (n->nredir.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +02009345 popredir(/*drop:*/ 0);
Eric Andersenc470f442003-07-28 09:56:35 +00009346 goto setstatus;
9347 case NCMD:
9348 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009349 checkexit:
Denys Vlasenkof415e212021-09-07 01:54:23 +02009350 checkexit = ~flags & EV_TESTED;
Eric Andersenc470f442003-07-28 09:56:35 +00009351 goto calleval;
9352 case NFOR:
9353 evalfn = evalfor;
9354 goto calleval;
9355 case NWHILE:
9356 case NUNTIL:
9357 evalfn = evalloop;
9358 goto calleval;
9359 case NSUBSHELL:
9360 case NBACKGND:
9361 evalfn = evalsubshell;
Denys Vlasenkocf98b0c2016-10-25 18:19:39 +02009362 goto checkexit;
Eric Andersenc470f442003-07-28 09:56:35 +00009363 case NPIPE:
9364 evalfn = evalpipe;
9365 goto checkexit;
9366 case NCASE:
9367 evalfn = evalcase;
9368 goto calleval;
9369 case NAND:
9370 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009371 case NSEMI: {
Eric Andersenc470f442003-07-28 09:56:35 +00009372#if NAND + 1 != NOR
9373#error NAND + 1 != NOR
9374#endif
9375#if NOR + 1 != NSEMI
9376#error NOR + 1 != NSEMI
9377#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00009378 unsigned is_or = n->type - NAND;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009379 status = evaltree(
Eric Andersenc470f442003-07-28 09:56:35 +00009380 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009381 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00009382 );
Denys Vlasenkobc1a0082016-10-02 15:31:33 +02009383 if ((!status) == is_or || evalskip)
Eric Andersenc470f442003-07-28 09:56:35 +00009384 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009385 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009386 evaln:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009387 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009388 calleval:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009389 status = evalfn(n, flags);
9390 goto setstatus;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009391 }
Eric Andersenc470f442003-07-28 09:56:35 +00009392 case NIF:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009393 status = evaltree(n->nif.test, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009394 if (evalskip)
9395 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009396 if (!status) {
Eric Andersenc470f442003-07-28 09:56:35 +00009397 n = n->nif.ifpart;
9398 goto evaln;
Denys Vlasenkof415e212021-09-07 01:54:23 +02009399 } else if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00009400 n = n->nif.elsepart;
9401 goto evaln;
9402 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009403 status = 0;
9404 goto setstatus;
Eric Andersenc470f442003-07-28 09:56:35 +00009405 case NDEFUN:
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009406 defun(n);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009407 /* Not necessary. To test it:
9408 * "false; f() { qwerty; }; echo $?" should print 0.
9409 */
9410 /* status = 0; */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009411 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00009412 exitstatus = status;
9413 break;
9414 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009415 out:
Denys Vlasenkob563f622010-09-25 17:15:13 +02009416 /* Order of checks below is important:
9417 * signal handlers trigger before exit caused by "set -e".
9418 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009419 dotrap();
9420
Denys Vlasenkof415e212021-09-07 01:54:23 +02009421 if (checkexit && status) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009422 if (trap[NTRAP_ERR] && !in_trap_ERR) {
9423 int err;
9424 struct jmploc *volatile savehandler = exception_handler;
9425 struct jmploc jmploc;
9426
9427 in_trap_ERR = 1;
9428 trap_depth++;
9429 err = setjmp(jmploc.loc);
9430 if (!err) {
9431 exception_handler = &jmploc;
9432 savestatus = exitstatus;
9433 evalstring(trap[NTRAP_ERR], 0);
9434 }
9435 trap_depth--;
9436 in_trap_ERR = 0;
9437
9438 exception_handler = savehandler;
9439 if (err && exception_type != EXERROR)
9440 longjmp(exception_handler->loc, 1);
9441
9442 exitstatus = savestatus;
9443 }
9444 if (eflag)
Denys Vlasenkof415e212021-09-07 01:54:23 +02009445 goto exexit;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009446 }
Denys Vlasenkof415e212021-09-07 01:54:23 +02009447 if (flags & EV_EXIT) {
9448 exexit:
Denys Vlasenkof977e002020-02-20 16:54:29 +01009449 raise_exception(EXEND);
Denys Vlasenkof415e212021-09-07 01:54:23 +02009450 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009451
Ron Yorstonf55161a2019-02-25 08:29:38 +00009452 popstackmark(&smark);
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009453 TRACE(("leaving evaltree (no interrupts)\n"));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009454 return exitstatus;
Eric Andersen62483552001-07-10 06:09:16 +00009455}
9456
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02009457static int
9458skiploop(void)
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009459{
9460 int skip = evalskip;
9461
9462 switch (skip) {
9463 case 0:
9464 break;
9465 case SKIPBREAK:
9466 case SKIPCONT:
9467 if (--skipcount <= 0) {
9468 evalskip = 0;
9469 break;
9470 }
9471 skip = SKIPBREAK;
9472 break;
9473 }
9474 return skip;
9475}
9476
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009477static int
Eric Andersenc470f442003-07-28 09:56:35 +00009478evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009479{
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009480 int skip;
Eric Andersencb57d552001-06-28 07:25:16 +00009481 int status;
9482
9483 loopnest++;
9484 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009485 flags &= EV_TESTED;
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009486 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009487 int i;
9488
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009489 i = evaltree(n->nbinary.ch1, EV_TESTED);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009490 skip = skiploop();
9491 if (skip == SKIPFUNC)
9492 status = i;
9493 if (skip)
9494 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00009495 if (n->type != NWHILE)
9496 i = !i;
9497 if (i != 0)
9498 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009499 status = evaltree(n->nbinary.ch2, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009500 skip = skiploop();
9501 } while (!(skip & ~SKIPCONT));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009502 loopnest--;
9503
9504 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009505}
9506
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009507static int
Eric Andersenc470f442003-07-28 09:56:35 +00009508evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009509{
9510 struct arglist arglist;
9511 union node *argp;
9512 struct strlist *sp;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009513 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009514
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009515 errlinno = lineno = n->ncase.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009516
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009517 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009518 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009519 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Ron Yorston549deab2015-05-18 09:57:51 +02009520 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
Eric Andersencb57d552001-06-28 07:25:16 +00009521 }
9522 *arglist.lastp = NULL;
9523
Eric Andersencb57d552001-06-28 07:25:16 +00009524 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009525 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009526 for (sp = arglist.list; sp; sp = sp->next) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009527 setvar0(n->nfor.var, sp->text);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009528 status = evaltree(n->nfor.body, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009529 if (skiploop() & ~SKIPCONT)
Eric Andersencb57d552001-06-28 07:25:16 +00009530 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009531 }
9532 loopnest--;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009533
9534 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009535}
9536
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009537static int
Eric Andersenc470f442003-07-28 09:56:35 +00009538evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009539{
9540 union node *cp;
9541 union node *patp;
9542 struct arglist arglist;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009543 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009544
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009545 errlinno = lineno = n->ncase.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009546
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009547 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009548 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00009549 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009550 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
9551 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00009552 if (casematch(patp, arglist.list->text)) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009553 /* Ensure body is non-empty as otherwise
9554 * EV_EXIT may prevent us from setting the
9555 * exit status.
9556 */
9557 if (evalskip == 0 && cp->nclist.body) {
9558 status = evaltree(cp->nclist.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00009559 }
9560 goto out;
9561 }
9562 }
9563 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009564 out:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009565 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009566}
9567
Eric Andersenc470f442003-07-28 09:56:35 +00009568/*
9569 * Kick off a subshell to evaluate a tree.
9570 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009571static int
Eric Andersenc470f442003-07-28 09:56:35 +00009572evalsubshell(union node *n, int flags)
9573{
9574 struct job *jp;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009575 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
Eric Andersenc470f442003-07-28 09:56:35 +00009576 int status;
9577
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009578 errlinno = lineno = n->nredir.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009579
Eric Andersenc470f442003-07-28 09:56:35 +00009580 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02009581 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00009582 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009583 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009584 if (backgnd == FORK_FG)
9585 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009586 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009587 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009588 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009589 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009590 flags |= EV_EXIT;
9591 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02009592 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009593 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00009594 redirect(n->nredir.redirect, 0);
9595 evaltreenr(n->nredir.n, flags);
9596 /* never returns */
9597 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009598 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009599 status = 0;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009600 if (backgnd == FORK_FG)
Eric Andersenc470f442003-07-28 09:56:35 +00009601 status = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009602 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009603 return status;
Eric Andersenc470f442003-07-28 09:56:35 +00009604}
9605
Eric Andersenc470f442003-07-28 09:56:35 +00009606/*
9607 * Compute the names of the files in a redirection list.
9608 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009609static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00009610static void
9611expredir(union node *n)
9612{
9613 union node *redir;
9614
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009615 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009616 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009617
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009618 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009619 fn.lastp = &fn.list;
9620 switch (redir->type) {
9621 case NFROMTO:
9622 case NFROM:
9623 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009624#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009625 case NTO2:
9626#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009627 case NCLOBBER:
9628 case NAPPEND:
9629 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02009630 TRACE(("expredir expanded to '%s'\n", fn.list->text));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009631#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009632 store_expfname:
9633#endif
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009634#if 0
9635// By the design of stack allocator, the loop of this kind:
9636// while true; do while true; do break; done </dev/null; done
9637// will look like a memory leak: ash plans to free expfname's
9638// of "/dev/null" as soon as it finishes running the loop
9639// (in this case, never).
9640// This "fix" is wrong:
Jon Tollefson4ba6c5d2012-11-13 19:26:53 +01009641 if (redir->nfile.expfname)
9642 stunalloc(redir->nfile.expfname);
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009643// It results in corrupted state of stacked allocations.
9644#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009645 redir->nfile.expfname = fn.list->text;
9646 break;
9647 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00009648 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00009649 if (redir->ndup.vname) {
Denys Vlasenkoe368d852020-02-16 19:02:22 +01009650 expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009651 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009652 ash_msg_and_raise_error("redir error");
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009653#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009654 if (!isdigit_str9(fn.list->text)) {
9655 /* >&file, not >&fd */
9656 if (redir->nfile.fd != 1) /* 123>&file - BAD */
9657 ash_msg_and_raise_error("redir error");
9658 redir->type = NTO2;
9659 goto store_expfname;
9660 }
9661#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009662 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009663 }
9664 break;
9665 }
9666 }
9667}
9668
Eric Andersencb57d552001-06-28 07:25:16 +00009669/*
Eric Andersencb57d552001-06-28 07:25:16 +00009670 * Evaluate a pipeline. All the processes in the pipeline are children
9671 * of the process creating the pipeline. (This differs from some versions
9672 * of the shell, which make the last process in a pipeline the parent
9673 * of all the rest.)
9674 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009675static int
Eric Andersenc470f442003-07-28 09:56:35 +00009676evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009677{
9678 struct job *jp;
9679 struct nodelist *lp;
9680 int pipelen;
9681 int prevfd;
9682 int pip[2];
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009683 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009684
Eric Andersenc470f442003-07-28 09:56:35 +00009685 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00009686 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009687 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00009688 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009689 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009690 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009691 if (n->npipe.pipe_backgnd == 0)
9692 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009693 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00009694 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009695 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009696 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00009697 pip[1] = -1;
9698 if (lp->next) {
9699 if (pipe(pip) < 0) {
9700 close(prevfd);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02009701 ash_msg_and_raise_perror("can't create pipe");
Eric Andersencb57d552001-06-28 07:25:16 +00009702 }
9703 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009704 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02009705 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009706 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009707 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009708 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00009709 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009710 if (prevfd > 0) {
9711 dup2(prevfd, 0);
9712 close(prevfd);
9713 }
9714 if (pip[1] > 1) {
9715 dup2(pip[1], 1);
9716 close(pip[1]);
9717 }
Eric Andersenc470f442003-07-28 09:56:35 +00009718 evaltreenr(lp->n, flags);
9719 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00009720 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009721 /* parent */
Eric Andersencb57d552001-06-28 07:25:16 +00009722 if (prevfd >= 0)
9723 close(prevfd);
9724 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00009725 /* Don't want to trigger debugging */
9726 if (pip[1] != -1)
9727 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009728 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009729 if (n->npipe.pipe_backgnd == 0) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009730 status = waitforjob(jp);
9731 TRACE(("evalpipe: job done exit status %d\n", status));
Eric Andersencb57d552001-06-28 07:25:16 +00009732 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009733 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009734
9735 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009736}
9737
Ron Yorston9e2a5662020-01-21 16:01:58 +00009738/* setinteractive needs this forward reference */
Denys Vlasenko27be0e82023-01-03 08:28:16 +01009739#if ENABLE_FEATURE_TAB_COMPLETION
Ron Yorston9e2a5662020-01-21 16:01:58 +00009740static const char *get_builtin_name(int i) FAST_FUNC;
9741#endif
9742
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009743/*
9744 * Controls whether the shell is interactive or not.
9745 */
9746static void
9747setinteractive(int on)
9748{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009749 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009750
9751 if (++on == is_interactive)
9752 return;
9753 is_interactive = on;
9754 setsignal(SIGINT);
9755 setsignal(SIGQUIT);
9756 setsignal(SIGTERM);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009757 if (is_interactive > 1) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009758#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009759 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009760 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009761
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009762 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009763 /* note: ash and hush share this string */
9764 out1fmt("\n\n%s %s\n"
Denys Vlasenko2ec34962014-09-08 16:52:39 +02009765 IF_ASH_HELP("Enter 'help' for a list of built-in commands.\n")
9766 "\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009767 bb_banner,
9768 "built-in shell (ash)"
9769 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009770 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009771 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009772#endif
Denys Vlasenko897475a2019-06-01 16:35:09 +02009773#if ENABLE_FEATURE_EDITING
Ron Yorston9e2a5662020-01-21 16:01:58 +00009774 if (!line_input_state) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009775 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
Denys Vlasenko27be0e82023-01-03 08:28:16 +01009776# if ENABLE_FEATURE_TAB_COMPLETION
Ron Yorston9e2a5662020-01-21 16:01:58 +00009777 line_input_state->get_exe_name = get_builtin_name;
Denys Vlasenko27be0e82023-01-03 08:28:16 +01009778# endif
9779# if EDITING_HAS_sh_get_var
Ron Yorston7d1c7d82022-03-24 12:17:25 +00009780 line_input_state->sh_get_var = lookupvar;
Denys Vlasenko27be0e82023-01-03 08:28:16 +01009781# endif
Ron Yorston9e2a5662020-01-21 16:01:58 +00009782 }
Denys Vlasenko897475a2019-06-01 16:35:09 +02009783#endif
9784 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009785}
9786
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009787static void
9788optschanged(void)
9789{
9790#if DEBUG
9791 opentrace();
9792#endif
9793 setinteractive(iflag);
9794 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009795#if ENABLE_FEATURE_EDITING_VI
Denys Vlasenko897475a2019-06-01 16:35:09 +02009796 if (line_input_state) {
9797 if (viflag)
9798 line_input_state->flags |= VI_MODE;
9799 else
9800 line_input_state->flags &= ~VI_MODE;
9801 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009802#else
9803 viflag = 0; /* forcibly keep the option off */
9804#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009805}
9806
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009807struct localvar_list {
9808 struct localvar_list *next;
9809 struct localvar *lv;
9810};
9811
9812static struct localvar_list *localvar_stack;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009813
9814/*
9815 * Called after a function returns.
9816 * Interrupts must be off.
9817 */
9818static void
Denys Vlasenko981a0562017-07-26 19:53:11 +02009819poplocalvars(int keep)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009820{
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009821 struct localvar_list *ll;
9822 struct localvar *lvp, *next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009823 struct var *vp;
9824
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009825 INT_OFF;
9826 ll = localvar_stack;
9827 localvar_stack = ll->next;
9828
9829 next = ll->lv;
9830 free(ll);
9831
9832 while ((lvp = next) != NULL) {
9833 next = lvp->next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009834 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02009835 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denys Vlasenko981a0562017-07-26 19:53:11 +02009836 if (keep) {
9837 int bits = VSTRFIXED;
9838
9839 if (lvp->flags != VUNSET) {
9840 if (vp->var_text == lvp->text)
9841 bits |= VTEXTFIXED;
9842 else if (!(lvp->flags & (VTEXTFIXED|VSTACK)))
9843 free((char*)lvp->text);
9844 }
9845
9846 vp->flags &= ~bits;
9847 vp->flags |= (lvp->flags & bits);
9848
9849 if ((vp->flags &
9850 (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
9851 unsetvar(vp->var_text);
9852 } else if (vp == NULL) { /* $- saved */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009853 memcpy(optlist, lvp->text, sizeof(optlist));
9854 free((char*)lvp->text);
9855 optschanged();
Denys Vlasenkod5b500c2017-07-26 19:25:40 +02009856 } else if (lvp->flags == VUNSET) {
9857 vp->flags &= ~(VSTRFIXED|VREADONLY);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009858 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009859 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009860 if (vp->var_func)
9861 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009862 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009863 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009864 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009865 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009866 }
9867 free(lvp);
9868 }
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009869 INT_ON;
9870}
9871
9872/*
9873 * Create a new localvar environment.
9874 */
Denys Vlasenko484fc202017-07-26 19:55:31 +02009875static struct localvar_list *
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009876pushlocalvars(int push)
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009877{
9878 struct localvar_list *ll;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009879 struct localvar_list *top;
9880
9881 top = localvar_stack;
9882 if (!push)
9883 goto out;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009884
9885 INT_OFF;
9886 ll = ckzalloc(sizeof(*ll));
9887 /*ll->lv = NULL; - zalloc did it */
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009888 ll->next = top;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009889 localvar_stack = ll;
9890 INT_ON;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009891 out:
9892 return top;
Denys Vlasenko484fc202017-07-26 19:55:31 +02009893}
9894
9895static void
9896unwindlocalvars(struct localvar_list *stop)
9897{
9898 while (localvar_stack != stop)
9899 poplocalvars(0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009900}
9901
9902static int
9903evalfun(struct funcnode *func, int argc, char **argv, int flags)
9904{
9905 volatile struct shparam saveparam;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009906 struct jmploc *volatile savehandler;
9907 struct jmploc jmploc;
9908 int e;
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02009909 int savelineno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009910 int savefuncline;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009911 char *savefuncname;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009912 char *savetrap = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009913
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009914 if (!Eflag) {
9915 savetrap = trap[NTRAP_ERR];
9916 trap[NTRAP_ERR] = NULL;
9917 }
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02009918 savelineno = lineno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009919 saveparam = shellparam;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009920 savefuncline = funcline;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009921 savefuncname = funcname;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +02009922 savehandler = exception_handler;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009923 e = setjmp(jmploc.loc);
9924 if (e) {
9925 goto funcdone;
9926 }
9927 INT_OFF;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009928 exception_handler = &jmploc;
Denis Vlasenko01631112007-12-16 17:20:38 +00009929 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009930 func->count++;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009931 funcname = func->n.ndefun.text;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009932 funcline = func->n.ndefun.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009933 INT_ON;
9934 shellparam.nparam = argc - 1;
9935 shellparam.p = argv + 1;
9936#if ENABLE_ASH_GETOPTS
9937 shellparam.optind = 1;
9938 shellparam.optoff = -1;
9939#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009940 evaltree(func->n.ndefun.body, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00009941 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009942 INT_OFF;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009943 funcname = savefuncname;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009944 if (savetrap) {
9945 if (!trap[NTRAP_ERR])
9946 trap[NTRAP_ERR] = savetrap;
9947 else
9948 free(savetrap);
9949 }
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009950 funcline = savefuncline;
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02009951 lineno = savelineno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009952 freefunc(func);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009953 freeparam(&shellparam);
9954 shellparam = saveparam;
9955 exception_handler = savehandler;
9956 INT_ON;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009957 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009958 return e;
9959}
9960
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009961/*
9962 * Make a variable a local variable. When a variable is made local, it's
9963 * value and flags are saved in a localvar structure. The saved values
9964 * will be restored when the shell function returns. We handle the name
Denys Vlasenkoe0a4e102015-05-13 02:20:14 +02009965 * "-" as a special case: it makes changes to "set +-options" local
9966 * (options will be restored on return from the function).
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009967 */
9968static void
Denys Vlasenko3e729102020-02-19 17:33:44 +01009969mklocal(char *name, int flags)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009970{
9971 struct localvar *lvp;
9972 struct var **vpp;
9973 struct var *vp;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009974 char *eq = strchr(name, '=');
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009975
9976 INT_OFF;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009977 /* Cater for duplicate "local". Examples:
9978 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
9979 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
9980 */
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009981 lvp = localvar_stack->lv;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009982 while (lvp) {
Eugene Rudoy1285aa62015-04-26 23:32:00 +02009983 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009984 if (eq)
9985 setvareq(name, 0);
9986 /* else:
9987 * it's a duplicate "local VAR" declaration, do nothing
9988 */
Denys Vlasenko06b11492016-11-04 16:43:18 +01009989 goto ret;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009990 }
9991 lvp = lvp->next;
9992 }
9993
9994 lvp = ckzalloc(sizeof(*lvp));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009995 if (LONE_DASH(name)) {
9996 char *p;
9997 p = ckmalloc(sizeof(optlist));
9998 lvp->text = memcpy(p, optlist, sizeof(optlist));
9999 vp = NULL;
10000 } else {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010001 vpp = hashvar(name);
10002 vp = *findvar(vpp, name);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010003 if (vp == NULL) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010004 /* variable did not exist yet */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010005 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +010010006 vp = setvareq(name, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010007 else
Denys Vlasenko3e729102020-02-19 17:33:44 +010010008 vp = setvar(name, NULL, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010009 lvp->flags = VUNSET;
10010 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010011 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010012 lvp->flags = vp->flags;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010013 /* make sure neither "struct var" nor string gets freed
10014 * during (un)setting:
10015 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010016 vp->flags |= VSTRFIXED|VTEXTFIXED;
10017 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +010010018 setvareq(name, flags);
Denys Vlasenko109ee5d2014-03-16 18:41:11 +010010019 else
10020 /* "local VAR" unsets VAR: */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010021 setvar0(name, NULL);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010022 }
10023 }
10024 lvp->vp = vp;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +020010025 lvp->next = localvar_stack->lv;
10026 localvar_stack->lv = lvp;
Denys Vlasenko06b11492016-11-04 16:43:18 +010010027 ret:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010028 INT_ON;
10029}
10030
10031/*
10032 * The "local" command.
10033 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010034static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010035localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010036{
10037 char *name;
10038
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +020010039 if (!localvar_stack)
Ron Yorstonef2386b2015-10-29 16:19:14 +000010040 ash_msg_and_raise_error("not in a function");
10041
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010042 argv = argptr;
10043 while ((name = *argv++) != NULL) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010044 mklocal(name, 0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010045 }
10046 return 0;
10047}
10048
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010049static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010050falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010051{
10052 return 1;
10053}
10054
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010055static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010056truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010057{
10058 return 0;
10059}
10060
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010061static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010062execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010063{
Denys Vlasenko6c149f42017-04-12 21:31:32 +020010064 optionarg = NULL;
10065 while (nextopt("a:") != '\0')
10066 /* nextopt() sets optionarg to "-a ARGV0" */;
10067
10068 argv = argptr;
10069 if (argv[0]) {
10070 char *prog;
10071
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010072 iflag = 0; /* exit on error */
10073 mflag = 0;
10074 optschanged();
Denys Vlasenkoe5814a52016-07-16 18:33:55 +020010075 /* We should set up signals for "exec CMD"
10076 * the same way as for "CMD" without "exec".
10077 * But optschanged->setinteractive->setsignal
10078 * still thought we are a root shell. Therefore, for example,
10079 * SIGQUIT is still set to IGN. Fix it:
10080 */
10081 shlvl++;
10082 setsignal(SIGQUIT);
10083 /*setsignal(SIGTERM); - unnecessary because of iflag=0 */
10084 /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */
10085 /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */
10086
Denys Vlasenko6c149f42017-04-12 21:31:32 +020010087 prog = argv[0];
10088 if (optionarg)
10089 argv[0] = optionarg;
10090 shellexec(prog, argv, pathval(), 0);
Denys Vlasenkoe5814a52016-07-16 18:33:55 +020010091 /* NOTREACHED */
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010092 }
10093 return 0;
10094}
10095
10096/*
10097 * The return command.
10098 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010099static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010100returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010101{
Denys Vlasenkocd24a502020-02-20 16:47:01 +010010102 int skip;
10103 int status;
10104
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010105 /*
10106 * If called outside a function, do what ksh does;
10107 * skip the rest of the file.
10108 */
Denys Vlasenkocd24a502020-02-20 16:47:01 +010010109 if (argv[1]) {
10110 skip = SKIPFUNC;
10111 status = number(argv[1]);
10112 } else {
10113 skip = SKIPFUNCDEF;
10114 status = exitstatus;
10115 }
10116 evalskip = skip;
10117
10118 return status;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010119}
10120
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010121/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010122static int breakcmd(int, char **) FAST_FUNC;
10123static int dotcmd(int, char **) FAST_FUNC;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010124static int evalcmd(int, char **, int) FAST_FUNC;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010125static int exitcmd(int, char **) FAST_FUNC;
10126static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010127#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010128static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010129#endif
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010130#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010131static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +000010132#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010133#if MAX_HISTORY
10134static int historycmd(int, char **) FAST_FUNC;
10135#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010136#if ENABLE_FEATURE_SH_MATH
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010137static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010138#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010139static int readcmd(int, char **) FAST_FUNC;
10140static int setcmd(int, char **) FAST_FUNC;
10141static int shiftcmd(int, char **) FAST_FUNC;
10142static int timescmd(int, char **) FAST_FUNC;
10143static int trapcmd(int, char **) FAST_FUNC;
10144static int umaskcmd(int, char **) FAST_FUNC;
10145static int unsetcmd(int, char **) FAST_FUNC;
10146static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010147
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010148#define BUILTIN_NOSPEC "0"
10149#define BUILTIN_SPECIAL "1"
10150#define BUILTIN_REGULAR "2"
10151#define BUILTIN_SPEC_REG "3"
10152#define BUILTIN_ASSIGN "4"
10153#define BUILTIN_SPEC_ASSG "5"
10154#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010155#define BUILTIN_SPEC_REG_ASSG "7"
10156
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010157/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko265062d2017-01-10 15:13:30 +010010158#if ENABLE_ASH_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010159static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010160#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010161#if ENABLE_ASH_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010162static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010163#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010164#if ENABLE_ASH_TEST || BASH_TEST2
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010165static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010166#endif
Shawn Landden58598eb2022-08-27 19:56:21 +020010167#if ENABLE_ASH_SLEEP
10168static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); }
10169#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +000010170
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010171/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010172static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010173 { BUILTIN_SPEC_REG "." , dotcmd },
10174 { BUILTIN_SPEC_REG ":" , truecmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010175#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010176 { BUILTIN_REGULAR "[" , testcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010177#endif
10178#if BASH_TEST2
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010179 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +000010180#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010181#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010182 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010183#endif
10184#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010185 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010186#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010187 { BUILTIN_SPEC_REG "break" , breakcmd },
10188 { BUILTIN_REGULAR "cd" , cdcmd },
10189 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010190#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010191 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010192#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010193 { BUILTIN_SPEC_REG "continue", breakcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010194#if ENABLE_ASH_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010195 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010196#endif
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010197 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010198 { BUILTIN_SPEC_REG "exec" , execcmd },
10199 { BUILTIN_SPEC_REG "exit" , exitcmd },
10200 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
10201 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010202#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010203 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010204#endif
10205#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010206 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010207#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010208 { BUILTIN_REGULAR "hash" , hashcmd },
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010209#if ENABLE_ASH_HELP
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010210 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010211#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010212#if MAX_HISTORY
10213 { BUILTIN_NOSPEC "history" , historycmd },
10214#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010215#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010216 { BUILTIN_REGULAR "jobs" , jobscmd },
10217 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010218#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010219#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010220 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010221#endif
Denys Vlasenko85241c72017-07-26 20:00:08 +020010222 { BUILTIN_SPEC_REG_ASSG "local" , localcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010223#if ENABLE_ASH_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010224 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +000010225#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010226 { BUILTIN_REGULAR "pwd" , pwdcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010227 { BUILTIN_REGULAR "read" , readcmd },
10228 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
10229 { BUILTIN_SPEC_REG "return" , returncmd },
10230 { BUILTIN_SPEC_REG "set" , setcmd },
10231 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Shawn Landden58598eb2022-08-27 19:56:21 +020010232#if ENABLE_ASH_SLEEP
10233 { BUILTIN_REGULAR "sleep" , sleepcmd },
10234#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010235#if BASH_SOURCE
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010236 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +020010237#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010238#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010239 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010240#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010241 { BUILTIN_SPEC_REG "times" , timescmd },
10242 { BUILTIN_SPEC_REG "trap" , trapcmd },
10243 { BUILTIN_REGULAR "true" , truecmd },
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010244 { BUILTIN_REGULAR "type" , typecmd },
10245 { BUILTIN_REGULAR "ulimit" , ulimitcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010246 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010247#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010248 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010249#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010250 { BUILTIN_SPEC_REG "unset" , unsetcmd },
10251 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010252};
10253
Denis Vlasenko80591b02008-03-25 07:49:43 +000010254/* Should match the above table! */
10255#define COMMANDCMD (builtintab + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010256 /* . : */ 2 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010257 /* [ */ 1 * ENABLE_ASH_TEST + \
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010258 /* [[ */ 1 * BASH_TEST2 + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010259 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10260 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
10261 /* break cd cddir */ 3)
10262#define EVALCMD (COMMANDCMD + \
10263 /* command */ 1 * ENABLE_ASH_CMDCMD + \
10264 /* continue */ 1 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010265 /* echo */ 1 * ENABLE_ASH_ECHO + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010266 0)
10267#define EXECCMD (EVALCMD + \
10268 /* eval */ 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010269
10270/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010271 * Search the table of builtin commands.
10272 */
Denys Vlasenko888527c2016-10-02 16:54:17 +020010273static int
10274pstrcmp1(const void *a, const void *b)
10275{
10276 return strcmp((char*)a, *(char**)b + 1);
10277}
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010278static struct builtincmd *
10279find_builtin(const char *name)
10280{
10281 struct builtincmd *bp;
10282
10283 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +000010284 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denys Vlasenko888527c2016-10-02 16:54:17 +020010285 pstrcmp1
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010286 );
10287 return bp;
10288}
10289
Denys Vlasenko27be0e82023-01-03 08:28:16 +010010290#if ENABLE_FEATURE_TAB_COMPLETION
Ron Yorston9e2a5662020-01-21 16:01:58 +000010291static const char * FAST_FUNC
10292get_builtin_name(int i)
10293{
10294 return /*i >= 0 &&*/ i < ARRAY_SIZE(builtintab) ? builtintab[i].name + 1 : NULL;
10295}
10296#endif
10297
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010298/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010299 * Execute a simple command.
10300 */
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010301static void unwindfiles(struct parsefile *stop);
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010302static int
10303isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +000010304{
10305 const char *q = endofname(p);
10306 if (p == q)
10307 return 0;
10308 return *q == '=';
10309}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010310static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010311bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010312{
10313 /* Preserve exitstatus of a previous possible redirection
10314 * as POSIX mandates */
10315 return back_exitstatus;
10316}
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010317static int
Eric Andersenc470f442003-07-28 09:56:35 +000010318evalcommand(union node *cmd, int flags)
10319{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010320 static const struct builtincmd null_bltin = {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010321 BUILTIN_REGULAR "", bltincmd
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010322 };
Denys Vlasenko484fc202017-07-26 19:55:31 +020010323 struct localvar_list *localvar_stop;
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010324 struct parsefile *file_stop;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010325 struct redirtab *redir_stop;
Eric Andersenc470f442003-07-28 09:56:35 +000010326 union node *argp;
10327 struct arglist arglist;
10328 struct arglist varlist;
10329 char **argv;
10330 int argc;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010331 struct strlist *osp;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010332 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +000010333 struct cmdentry cmdentry;
10334 struct job *jp;
10335 char *lastarg;
10336 const char *path;
10337 int spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010338 int cmd_flag;
Eric Andersenc470f442003-07-28 09:56:35 +000010339 int status;
10340 char **nargv;
Denis Vlasenko34c73c42008-08-16 11:48:02 +000010341 smallint cmd_is_exec;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010342 int vflags;
10343 int vlocal;
Eric Andersenc470f442003-07-28 09:56:35 +000010344
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010345 errlinno = lineno = cmd->ncmd.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010346
Eric Andersenc470f442003-07-28 09:56:35 +000010347 /* First expand the arguments. */
10348 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
Ron Yorston4a36ef12021-08-30 20:31:42 +010010349#if BASH_PROCESS_SUBST
10350 redir_stop = redirlist;
10351#endif
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010352 file_stop = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000010353 back_exitstatus = 0;
10354
10355 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010356 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +000010357 varlist.lastp = &varlist.list;
10358 *varlist.lastp = NULL;
10359 arglist.lastp = &arglist.list;
10360 *arglist.lastp = NULL;
10361
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010362 cmd_flag = 0;
10363 cmd_is_exec = 0;
10364 spclbltin = -1;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010365 vflags = 0;
10366 vlocal = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010367 path = NULL;
10368
Eric Andersenc470f442003-07-28 09:56:35 +000010369 argc = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010370 argp = cmd->ncmd.args;
10371 osp = fill_arglist(&arglist, &argp);
10372 if (osp) {
10373 int pseudovarflag = 0;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010374
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010375 for (;;) {
10376 find_command(arglist.list->text, &cmdentry,
10377 cmd_flag | DO_REGBLTIN, pathval());
Paul Foxc3850c82005-07-20 18:23:39 +000010378
Denys Vlasenko3e729102020-02-19 17:33:44 +010010379 vlocal++;
10380
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010381 /* implement bltin and command here */
10382 if (cmdentry.cmdtype != CMDBUILTIN)
10383 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010384
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010385 pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
10386 if (spclbltin < 0) {
10387 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
Denys Vlasenko3e729102020-02-19 17:33:44 +010010388 vlocal = !spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010389 }
10390 cmd_is_exec = cmdentry.u.cmd == EXECCMD;
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010391#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010392 if (cmdentry.u.cmd != COMMANDCMD)
10393 break;
Paul Foxc3850c82005-07-20 18:23:39 +000010394
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010395 cmd_flag = parse_command_args(&arglist, &argp, &path);
10396 if (!cmd_flag)
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010397#endif
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010398 break;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010399 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010400
10401 for (; argp; argp = argp->narg.next)
10402 expandarg(argp, &arglist,
10403 pseudovarflag &&
10404 isassignment(argp->narg.text) ?
10405 EXP_VARTILDE : EXP_FULL | EXP_TILDE);
10406
10407 for (sp = arglist.list; sp; sp = sp->next)
10408 argc++;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010409
10410 if (cmd_is_exec && argc > 1)
10411 vflags = VEXPORT;
Eric Andersenc470f442003-07-28 09:56:35 +000010412 }
10413
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010414 localvar_stop = pushlocalvars(vlocal);
10415
Denys Vlasenko65a8b852016-10-26 22:29:11 +020010416 /* Reserve one extra spot at the front for shellexec. */
10417 nargv = stalloc(sizeof(char *) * (argc + 2));
10418 argv = ++nargv;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010419 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +000010420 TRACE(("evalcommand arg: %s\n", sp->text));
10421 *nargv++ = sp->text;
10422 }
10423 *nargv = NULL;
10424
10425 lastarg = NULL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010426 if (iflag && funcline == 0 && argc > 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010427 lastarg = nargv[-1];
10428
10429 expredir(cmd->ncmd.redirect);
Ron Yorston4a36ef12021-08-30 20:31:42 +010010430#if !BASH_PROCESS_SUBST
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010431 redir_stop = pushredir(cmd->ncmd.redirect);
Ron Yorston4a36ef12021-08-30 20:31:42 +010010432#else
10433 pushredir(cmd->ncmd.redirect);
10434#endif
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010435 preverrout_fd = 2;
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +020010436 if (BASH_XTRACEFD && xflag) {
10437 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed.
10438 * we do not emulate this. We only use its value.
10439 */
10440 const char *xtracefd = lookupvar("BASH_XTRACEFD");
10441 if (xtracefd && is_number(xtracefd))
10442 preverrout_fd = atoi(xtracefd);
10443
10444 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010445 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +000010446
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010447 if (status) {
10448 bail:
10449 exitstatus = status;
10450
10451 /* We have a redirection error. */
10452 if (spclbltin > 0)
10453 raise_exception(EXERROR);
10454
10455 goto out;
10456 }
10457
Eric Andersenc470f442003-07-28 09:56:35 +000010458 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10459 struct strlist **spp;
Eric Andersenc470f442003-07-28 09:56:35 +000010460
10461 spp = varlist.lastp;
10462 expandarg(argp, &varlist, EXP_VARTILDE);
10463
Denys Vlasenko3e729102020-02-19 17:33:44 +010010464 if (vlocal)
10465 mklocal((*spp)->text, VEXPORT);
10466 else
10467 setvareq((*spp)->text, vflags);
Eric Andersenc470f442003-07-28 09:56:35 +000010468 }
10469
10470 /* Print the command if xflag is set. */
Denys Vlasenkoeb607772021-09-09 16:26:41 +020010471 if (xflag && !inps4) {
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010472 const char *pfx = "";
Eric Andersenc470f442003-07-28 09:56:35 +000010473
Denys Vlasenkoeb607772021-09-09 16:26:41 +020010474 inps4 = 1;
Denys Vlasenko46999802017-07-29 21:12:29 +020010475 fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX));
Denys Vlasenkoeb607772021-09-09 16:26:41 +020010476 inps4 = 0;
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010477
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010478 sp = varlist.list;
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010479 while (sp) {
10480 char *varval = sp->text;
10481 char *eq = strchrnul(varval, '=');
10482 if (*eq)
10483 eq++;
10484 fdprintf(preverrout_fd, "%s%.*s%s",
10485 pfx,
10486 (int)(eq - varval), varval,
10487 maybe_single_quote(eq)
10488 );
10489 sp = sp->next;
10490 pfx = " ";
10491 }
10492
10493 sp = arglist.list;
10494 while (sp) {
10495 fdprintf(preverrout_fd, "%s%s",
10496 pfx,
10497 /* always quote if matches reserved word: */
10498 findkwd(sp->text)
10499 ? single_quote(sp->text)
10500 : maybe_single_quote(sp->text)
10501 );
10502 sp = sp->next;
10503 pfx = " ";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010504 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010505 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010506 }
10507
Eric Andersenc470f442003-07-28 09:56:35 +000010508 /* Now locate the command. */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010509 if (cmdentry.cmdtype != CMDBUILTIN
10510 || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
10511 ) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010512 path = path ? path : pathval();
10513 find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path);
Eric Andersenc470f442003-07-28 09:56:35 +000010514 }
10515
Denys Vlasenkod81af722020-02-18 14:28:30 +010010516 jp = NULL;
10517
Eric Andersenc470f442003-07-28 09:56:35 +000010518 /* Execute the command. */
10519 switch (cmdentry.cmdtype) {
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010520 case CMDUNKNOWN:
10521 status = 127;
10522 flush_stdout_stderr();
10523 goto bail;
10524
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010525 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +000010526
Denys Vlasenko1750d3a2018-01-15 00:41:04 +010010527#if ENABLE_FEATURE_SH_STANDALONE \
10528 && ENABLE_FEATURE_SH_NOFORK \
10529 && NUM_APPLETS > 1
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010530/* (1) BUG: if variables are set, we need to fork, or save/restore them
10531 * around run_nofork_applet() call.
10532 * (2) Should this check also be done in forkshell()?
10533 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
10534 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000010535 /* find_command() encodes applet_no as (-2 - applet_no) */
10536 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010537 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010538 char **sv_environ;
10539
10540 INT_OFF;
10541 sv_environ = environ;
10542 environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL);
Denys Vlasenkod329e342017-08-04 14:50:03 +020010543 /*
10544 * Run <applet>_main().
10545 * Signals (^C) can't interrupt here.
10546 * Otherwise we can mangle stdio or malloc internal state.
10547 * This makes applets which can run for a long time
10548 * and/or wait for user input ineligible for NOFORK:
10549 * for example, "yes" or "rm" (rm -i waits for input).
10550 */
Ron Yorstond5bfe262020-02-20 08:23:03 +000010551 exitstatus = run_nofork_applet(applet_no, argv);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010552 environ = sv_environ;
Denys Vlasenkod329e342017-08-04 14:50:03 +020010553 /*
10554 * Try enabling NOFORK for "yes" applet.
10555 * ^C _will_ stop it (write returns EINTR),
10556 * but this causes stdout FILE to be stuck
10557 * and needing clearerr(). What if other applets
10558 * also can get EINTRs? Do we need to switch
10559 * our signals to SA_RESTART?
10560 */
10561 /*clearerr(stdout);*/
10562 INT_ON;
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010563 break;
10564 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010565#endif
Denys Vlasenkocfd392b2017-08-03 19:56:29 +020010566 /* Can we avoid forking? For example, very last command
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010567 * in a script or a subshell does not need forking,
10568 * we can just exec it.
10569 */
Denys Vlasenko238bf182010-05-18 15:49:07 +020010570 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010571 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010572 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +010010573 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +000010574 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010575 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +020010576 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +000010577 break;
10578 }
Denys Vlasenko238bf182010-05-18 15:49:07 +020010579 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010580 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +020010581 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +000010582 }
Denys Vlasenkoe139ae32017-04-12 21:02:33 +020010583 shellexec(argv[0], argv, path, cmdentry.u.index);
Eric Andersenc470f442003-07-28 09:56:35 +000010584 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010585 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +000010586 case CMDBUILTIN:
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010587 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)
10588 && !(exception_type == EXERROR && spclbltin <= 0)
10589 ) {
Denys Vlasenkoc0663c72016-10-27 21:09:01 +020010590 raise:
10591 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010592 }
Denys Vlasenkod81af722020-02-18 14:28:30 +010010593 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010594
10595 case CMDFUNCTION:
Eric Andersenc470f442003-07-28 09:56:35 +000010596 if (evalfun(cmdentry.u.func, argc, argv, flags))
10597 goto raise;
10598 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010599 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +000010600
Denys Vlasenkod81af722020-02-18 14:28:30 +010010601 status = waitforjob(jp);
Ron Yorston6cda0b02020-02-21 16:16:56 +000010602 if (jp)
10603 TRACE(("forked child exited with %d\n", status));
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010604 FORCE_INT_ON;
Denys Vlasenkod81af722020-02-18 14:28:30 +010010605
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010606 out:
Denys Vlasenkoeaf94362016-10-25 21:46:03 +020010607 if (cmd->ncmd.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +020010608 popredir(/*drop:*/ cmd_is_exec);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010609 unwindredir(redir_stop);
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010610 unwindfiles(file_stop);
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010611 unwindlocalvars(localvar_stop);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010612 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +000010613 /* dsl: I think this is intended to be used to support
10614 * '_' in 'vi' command mode during line editing...
10615 * However I implemented that within libedit itself.
10616 */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010617 setvar0("_", lastarg);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010618 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010619
10620 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000010621}
10622
10623static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010624evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags)
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010625{
Eric Andersenc470f442003-07-28 09:56:35 +000010626 char *volatile savecmdname;
10627 struct jmploc *volatile savehandler;
10628 struct jmploc jmploc;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010629 int status;
Eric Andersenc470f442003-07-28 09:56:35 +000010630 int i;
10631
10632 savecmdname = commandname;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +020010633 savehandler = exception_handler;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010634 i = setjmp(jmploc.loc);
10635 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +000010636 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010637 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +000010638 commandname = argv[0];
10639 argptr = argv + 1;
10640 optptr = NULL; /* initialize nextopt */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010641 if (cmd == EVALCMD)
10642 status = evalcmd(argc, argv, flags);
10643 else
10644 status = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010645 flush_stdout_stderr();
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010646 status |= ferror(stdout);
10647 exitstatus = status;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010648 cmddone:
Rob Landleyf296f0b2006-07-06 01:09:21 +000010649 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000010650 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010651 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010652
10653 return i;
10654}
10655
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010656static int
10657goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010658{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +020010659 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010660}
10661
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010662
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010663/*
10664 * Search for a command. This is called before we fork so that the
10665 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010666 * the child. The check for "goodname" is an overly conservative
10667 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010668 */
Eric Andersenc470f442003-07-28 09:56:35 +000010669static void
10670prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010671{
10672 struct cmdentry entry;
10673
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010674 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
10675 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010676}
10677
Eric Andersencb57d552001-06-28 07:25:16 +000010678
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010679/* ============ Builtin commands
10680 *
10681 * Builtin commands whose functions are closely tied to evaluation
10682 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +000010683 */
10684
10685/*
Eric Andersencb57d552001-06-28 07:25:16 +000010686 * Handle break and continue commands. Break, continue, and return are
10687 * all handled by setting the evalskip flag. The evaluation routines
10688 * above all check this flag, and if it is set they start skipping
10689 * commands rather than executing them. The variable skipcount is
10690 * the number of loops to break/continue, or the number of function
10691 * levels to return. (The latter is always 1.) It should probably
10692 * be an error to break out of more loops than exist, but it isn't
10693 * in the standard shell so we don't make it one here.
10694 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010695static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010696breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010697{
Denis Vlasenko68404f12008-03-17 09:00:54 +000010698 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010699
Aaron Lehmann2aef3a62001-12-31 06:03:12 +000010700 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020010701 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +000010702 if (n > loopnest)
10703 n = loopnest;
10704 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010705 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +000010706 skipcount = n;
10707 }
10708 return 0;
10709}
10710
Eric Andersenc470f442003-07-28 09:56:35 +000010711
Denys Vlasenko70392332016-10-27 02:31:55 +020010712/*
Eric Andersen90898442003-08-06 11:20:52 +000010713 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +000010714 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010715
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010716enum {
10717 INPUT_PUSH_FILE = 1,
10718 INPUT_NOFILE_OK = 2,
10719};
Eric Andersencb57d552001-06-28 07:25:16 +000010720
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010721static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010722/* values of checkkwd variable */
10723#define CHKALIAS 0x1
10724#define CHKKWD 0x2
10725#define CHKNL 0x4
Denys Vlasenkoa7328982017-07-29 19:57:28 +020010726#define CHKEOFMARK 0x8
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010727
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010728/*
10729 * Push a string back onto the input at this current parsefile level.
10730 * We handle aliases this way.
10731 */
10732#if !ENABLE_ASH_ALIAS
10733#define pushstring(s, ap) pushstring(s)
10734#endif
10735static void
10736pushstring(char *s, struct alias *ap)
10737{
10738 struct strpush *sp;
10739 int len;
10740
10741 len = strlen(s);
10742 INT_OFF;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010743 if (g_parsefile->strpush || g_parsefile->spfree) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010744 sp = ckzalloc(sizeof(*sp));
10745 sp->prev = g_parsefile->strpush;
10746 } else {
10747 sp = &(g_parsefile->basestrpush);
10748 }
10749 g_parsefile->strpush = sp;
10750 sp->prev_string = g_parsefile->next_to_pgetc;
10751 sp->prev_left_in_line = g_parsefile->left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010752 sp->unget = g_parsefile->unget;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010753 sp->spfree = g_parsefile->spfree;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010754 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010755#if ENABLE_ASH_ALIAS
10756 sp->ap = ap;
10757 if (ap) {
10758 ap->flag |= ALIASINUSE;
10759 sp->string = s;
10760 }
10761#endif
10762 g_parsefile->next_to_pgetc = s;
10763 g_parsefile->left_in_line = len;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010764 g_parsefile->unget = 0;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010765 g_parsefile->spfree = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010766 INT_ON;
10767}
10768
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010769static void popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +000010770{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010771 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +000010772
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010773 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010774#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010775 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010776 if (g_parsefile->next_to_pgetc[-1] == ' '
10777 || g_parsefile->next_to_pgetc[-1] == '\t'
10778 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010779 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010780 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010781 if (sp->string != sp->ap->val) {
10782 free(sp->string);
10783 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010784 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010785#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010786 g_parsefile->next_to_pgetc = sp->prev_string;
10787 g_parsefile->left_in_line = sp->prev_left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010788 g_parsefile->unget = sp->unget;
10789 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010790 g_parsefile->strpush = sp->prev;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010791 g_parsefile->spfree = sp;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010792 INT_ON;
10793}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010794
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010795static int
10796preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010797{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010798 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +000010799 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010800
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010801 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +000010802#if ENABLE_FEATURE_EDITING
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010803 /* retry: */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010804 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Ron Yorston61d6ae22015-04-19 10:50:25 +010010805 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010806 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010807# if ENABLE_ASH_IDLE_TIMEOUT
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010808 int timeout = -1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020010809 const char *tmout_var = lookupvar("TMOUT");
10810 if (tmout_var) {
10811 timeout = atoi(tmout_var) * 1000;
10812 if (timeout <= 0)
10813 timeout = -1;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010814 }
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010815 line_input_state->timeout = timeout;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010816# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010817# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010818 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010819# endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +020010820 reinit_unicode_for_ash();
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010821 again:
Denys Vlasenko12566e72022-01-17 03:02:40 +010010822 /* For shell, LI_INTERRUPTIBLE is set:
10823 * read_line_input will abort on either
10824 * getting EINTR in poll(), or if it sees bb_got_signal != 0
10825 * (IOW: if signal arrives before poll() is reached).
10826 * Interactive testcases:
10827 * (while kill -INT $$; do sleep 1; done) &
10828 * #^^^ prints ^C, prints prompt, repeats
10829 * trap 'echo I' int; (while kill -INT $$; do sleep 1; done) &
10830 * #^^^ prints ^C, prints "I", prints prompt, repeats
10831 * trap 'echo T' term; (while kill $$; do sleep 1; done) &
10832 * #^^^ prints "T", prints prompt, repeats
10833 * #(bash 5.0.17 exits after first "T", looks like a bug)
10834 */
10835 bb_got_signal = 0;
10836 INT_OFF; /* no longjmp'ing out of read_line_input please */
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010837 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
Denys Vlasenko12566e72022-01-17 03:02:40 +010010838 if (bb_got_signal == SIGINT)
10839 write(STDOUT_FILENO, "^C\n", 3);
10840 INT_ON; /* here non-blocked SIGINT will longjmp */
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010841 if (nr == 0) {
Denys Vlasenko4b89d512016-11-25 03:41:03 +010010842 /* ^C pressed, "convert" to SIGINT */
Denys Vlasenko12566e72022-01-17 03:02:40 +010010843 write(STDOUT_FILENO, "^C\n", 3);
10844 raise(SIGINT); /* here non-blocked SIGINT will longjmp */
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010845 /* raise(SIGINT) did not work! (e.g. if SIGINT
Denys Vlasenko931c55f2022-01-13 12:50:48 +010010846 * is SIG_IGNed on startup, it stays SIG_IGNed)
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010847 */
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010848 if (trap[SIGINT]) {
Denys Vlasenko12566e72022-01-17 03:02:40 +010010849 empty_line_input:
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010850 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010851 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010852 return 1;
10853 }
Denys Vlasenko8660aeb2016-11-24 17:44:02 +010010854 exitstatus = 128 + SIGINT;
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010855 /* bash behavior on ^C + ignored SIGINT: */
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010856 goto again;
Eric Andersenc470f442003-07-28 09:56:35 +000010857 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010858 if (nr < 0) {
10859 if (errno == 0) {
Denys Vlasenko12566e72022-01-17 03:02:40 +010010860 /* ^D pressed */
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010861 nr = 0;
10862 }
Denys Vlasenko12566e72022-01-17 03:02:40 +010010863 else if (errno == EINTR) { /* got signal? */
10864 if (bb_got_signal != SIGINT)
10865 write(STDOUT_FILENO, "\n", 1);
10866 goto empty_line_input;
10867 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010868# if ENABLE_ASH_IDLE_TIMEOUT
10869 else if (errno == EAGAIN && timeout > 0) {
Denys Vlasenkod60752f2015-10-07 22:42:45 +020010870 puts("\007timed out waiting for input: auto-logout");
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010871 exitshell();
10872 }
10873# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010874 }
Eric Andersencb57d552001-06-28 07:25:16 +000010875 }
10876#else
Ron Yorston61d6ae22015-04-19 10:50:25 +010010877 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +000010878#endif
10879
Denys Vlasenko80c5b682011-05-08 21:21:10 +020010880#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +000010881 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010882 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +000010883 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +000010884 if (flags >= 0 && (flags & O_NONBLOCK)) {
10885 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +000010886 if (fcntl(0, F_SETFL, flags) >= 0) {
10887 out2str("sh: turning off NDELAY mode\n");
10888 goto retry;
10889 }
10890 }
10891 }
10892 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +000010893#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010894 return nr;
10895}
10896
10897/*
10898 * Refill the input buffer and return the next input character:
10899 *
10900 * 1) If a string was pushed back on the input, pop it;
Ron Yorston0beee202021-09-12 11:20:33 +010010901 * 2) If we are reading from a string we can't refill the buffer, return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +020010902 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +000010903 * 4) Process input up to the next newline, deleting nul characters.
10904 */
Denis Vlasenko727752d2008-11-28 03:41:47 +000010905//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
10906#define pgetc_debug(...) ((void)0)
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010907static int __pgetc(void);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010908static int
Eric Andersenc470f442003-07-28 09:56:35 +000010909preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010910{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010911 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010912 int more;
Eric Andersencb57d552001-06-28 07:25:16 +000010913
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010914 if (unlikely(g_parsefile->strpush)) {
Eric Andersencb57d552001-06-28 07:25:16 +000010915 popstring();
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010916 return __pgetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010917 }
Denis Vlasenko727752d2008-11-28 03:41:47 +000010918
Ron Yorston0beee202021-09-12 11:20:33 +010010919 if (g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010920 pgetc_debug("preadbuffer PEOF1");
Eric Andersencb57d552001-06-28 07:25:16 +000010921 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010922 }
Eric Andersencb57d552001-06-28 07:25:16 +000010923
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010924 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010925 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010926 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010927 again:
10928 more = preadfd();
10929 if (more <= 0) {
Ron Yorston0beee202021-09-12 11:20:33 +010010930 g_parsefile->left_in_buffer = g_parsefile->left_in_line = 0;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010931 pgetc_debug("preadbuffer PEOF2");
Eric Andersencb57d552001-06-28 07:25:16 +000010932 return PEOF;
10933 }
10934 }
10935
Denis Vlasenko727752d2008-11-28 03:41:47 +000010936 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010937 * Set g_parsefile->left_in_line
10938 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010939 * NUL chars are deleted.
10940 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010941 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010942 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010943 char c;
Eric Andersencb57d552001-06-28 07:25:16 +000010944
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010945 more--;
Eric Andersenc470f442003-07-28 09:56:35 +000010946
Denis Vlasenko727752d2008-11-28 03:41:47 +000010947 c = *q;
10948 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010949 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010950 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010951 q++;
10952 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010953 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010954 break;
10955 }
Eric Andersencb57d552001-06-28 07:25:16 +000010956 }
10957
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010958 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010959 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
10960 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010961 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010962 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010963 }
10964 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010965 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +000010966
Eric Andersencb57d552001-06-28 07:25:16 +000010967 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010968 char save = *q;
10969 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010970 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010971 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +000010972 }
10973
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010974 pgetc_debug("preadbuffer at %d:%p'%s'",
10975 g_parsefile->left_in_line,
10976 g_parsefile->next_to_pgetc,
10977 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +010010978 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010979}
10980
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010981static void
10982nlprompt(void)
10983{
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020010984 if (trap_depth == 0)
10985 g_parsefile->linno++;
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010986 setprompt_if(doprompt, 2);
10987}
10988static void
10989nlnoprompt(void)
10990{
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020010991 if (trap_depth == 0)
10992 g_parsefile->linno++;
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010993 needprompt = doprompt;
10994}
10995
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010996static void freestrings(struct strpush *sp)
10997{
10998 INT_OFF;
10999 do {
11000 struct strpush *psp;
Denys Vlasenko5b026d12021-09-28 17:41:56 +020011001#if ENABLE_ASH_ALIAS
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011002 if (sp->ap) {
11003 sp->ap->flag &= ~ALIASINUSE;
11004 if (sp->ap->flag & ALIASDEAD) {
11005 unalias(sp->ap->name);
11006 }
11007 }
Denys Vlasenko5b026d12021-09-28 17:41:56 +020011008#endif
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011009 psp = sp;
11010 sp = sp->spfree;
11011
11012 if (psp != &(g_parsefile->basestrpush))
11013 free(psp);
11014 } while (sp);
11015
11016 g_parsefile->spfree = NULL;
11017 INT_ON;
11018}
11019
11020static int __pgetc(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011021{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011022 int c;
11023
11024 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011025 g_parsefile->left_in_line,
11026 g_parsefile->next_to_pgetc,
11027 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011028 if (g_parsefile->unget)
11029 return g_parsefile->lastc[--g_parsefile->unget];
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011030
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011031 if (--g_parsefile->left_in_line >= 0)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +010011032 c = (unsigned char)*g_parsefile->next_to_pgetc++;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011033 else
11034 c = preadbuffer();
11035
11036 g_parsefile->lastc[1] = g_parsefile->lastc[0];
11037 g_parsefile->lastc[0] = c;
11038
11039 return c;
11040}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011041
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011042/*
11043 * Read a character from the script, returning PEOF on end of file.
11044 * Nul characters in the input are silently discarded.
11045 */
11046static int pgetc(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011047{
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011048 struct strpush *sp = g_parsefile->spfree;
11049
11050 if (unlikely(sp))
11051 freestrings(sp);
11052
11053 return __pgetc();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011054}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011055
11056/*
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011057 * Undo a call to pgetc. Only two characters may be pushed back.
Eric Andersenc470f442003-07-28 09:56:35 +000011058 * PEOF may be pushed back.
11059 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011060static void
Eric Andersenc470f442003-07-28 09:56:35 +000011061pungetc(void)
11062{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011063 g_parsefile->unget++;
Eric Andersencb57d552001-06-28 07:25:16 +000011064}
11065
Denys Vlasenko73c3e072016-09-29 17:17:04 +020011066/* This one eats backslash+newline */
11067static int
11068pgetc_eatbnl(void)
11069{
11070 int c;
11071
11072 while ((c = pgetc()) == '\\') {
11073 if (pgetc() != '\n') {
11074 pungetc();
11075 break;
11076 }
11077
Denys Vlasenkoce332a22016-10-02 23:47:34 +020011078 nlprompt();
Denys Vlasenko73c3e072016-09-29 17:17:04 +020011079 }
11080
11081 return c;
11082}
11083
Denys Vlasenko216913c2018-04-02 12:35:04 +020011084struct synstack {
11085 smalluint syntax;
11086 uint8_t innerdq :1;
11087 uint8_t varpushed :1;
11088 uint8_t dblquote :1;
11089 int varnest; /* levels of variables expansion */
11090 int dqvarnest; /* levels of variables expansion within double quotes */
11091 int parenlevel; /* levels of parens in arithmetic */
11092 struct synstack *prev;
11093 struct synstack *next;
11094};
11095
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010011096static int
11097pgetc_top(struct synstack *stack)
11098{
11099 return stack->syntax == SQSYNTAX ? pgetc() : pgetc_eatbnl();
11100}
11101
Denys Vlasenko216913c2018-04-02 12:35:04 +020011102static void
11103synstack_push(struct synstack **stack, struct synstack *next, int syntax)
11104{
11105 memset(next, 0, sizeof(*next));
11106 next->syntax = syntax;
11107 next->next = *stack;
11108 (*stack)->prev = next;
11109 *stack = next;
11110}
11111
11112static ALWAYS_INLINE void
11113synstack_pop(struct synstack **stack)
11114{
11115 *stack = (*stack)->next;
11116}
11117
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011118/*
11119 * To handle the "." command, a stack of input files is used. Pushfile
11120 * adds a new entry to the stack and popfile restores the previous level.
11121 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011122static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011123pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +000011124{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011125 struct parsefile *pf;
11126
Denis Vlasenko597906c2008-02-20 16:38:54 +000011127 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011128 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011129 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011130 /*pf->strpush = NULL; - ckzalloc did it */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011131 /*pf->spfree = NULL;*/
Denis Vlasenko597906c2008-02-20 16:38:54 +000011132 /*pf->basestrpush.prev = NULL;*/
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011133 /*pf->unget = 0;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011134 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011135}
11136
11137static void
11138popfile(void)
11139{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011140 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000011141
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010011142 if (pf == &basepf)
11143 return;
11144
Denis Vlasenkob012b102007-02-19 22:43:01 +000011145 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011146 if (pf->pf_fd >= 0)
11147 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +000011148 free(pf->buf);
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011149 if (g_parsefile->spfree)
11150 freestrings(g_parsefile->spfree);
11151 while (pf->strpush) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011152 popstring();
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011153 freestrings(g_parsefile->spfree);
11154 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011155 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011156 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011157 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011158}
11159
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020011160static void
11161unwindfiles(struct parsefile *stop)
11162{
11163 while (g_parsefile != stop)
11164 popfile();
11165}
11166
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011167/*
11168 * Return to top level.
11169 */
11170static void
11171popallfiles(void)
11172{
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020011173 unwindfiles(&basepf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011174}
11175
11176/*
11177 * Close the file(s) that the shell is reading commands from. Called
11178 * after a fork is done.
11179 */
11180static void
11181closescript(void)
11182{
11183 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011184 if (g_parsefile->pf_fd > 0) {
11185 close(g_parsefile->pf_fd);
11186 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011187 }
11188}
11189
11190/*
11191 * Like setinputfile, but takes an open file descriptor. Call this with
11192 * interrupts off.
11193 */
11194static void
11195setinputfd(int fd, int push)
11196{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011197 if (push) {
11198 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +000011199 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011200 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011201 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011202 if (g_parsefile->buf == NULL)
11203 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011204 g_parsefile->left_in_buffer = 0;
11205 g_parsefile->left_in_line = 0;
11206 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011207}
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011208
Eric Andersenc470f442003-07-28 09:56:35 +000011209/*
11210 * Set the input to take input from a file. If push is set, push the
11211 * old input onto the stack first.
11212 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011213static int
11214setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000011215{
11216 int fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011217
Denis Vlasenkob012b102007-02-19 22:43:01 +000011218 INT_OFF;
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011219 fd = open(fname, O_RDONLY | O_CLOEXEC);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011220 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011221 if (flags & INPUT_NOFILE_OK)
11222 goto out;
Denys Vlasenkob7adf7a2016-10-25 17:00:13 +020011223 exitstatus = 127;
Johannes Schindelin20a63b22017-08-22 22:03:17 +020011224 ash_msg_and_raise_perror("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011225 }
Denys Vlasenko64774602016-10-26 15:24:30 +020011226 if (fd < 10)
11227 fd = savefd(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011228 else if (O_CLOEXEC == 0) /* old libc */
Denys Vlasenkoe19923f2016-10-26 15:38:44 +020011229 close_on_exec_on(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011230
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011231 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011232 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +000011233 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011234 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011235}
11236
Eric Andersencb57d552001-06-28 07:25:16 +000011237/*
11238 * Like setinputfile, but takes input from a string.
11239 */
Eric Andersenc470f442003-07-28 09:56:35 +000011240static void
11241setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000011242{
Denis Vlasenkob012b102007-02-19 22:43:01 +000011243 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011244 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011245 g_parsefile->next_to_pgetc = string;
11246 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011247 g_parsefile->buf = NULL;
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +020011248 g_parsefile->linno = lineno;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011249 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011250}
11251
11252
Denys Vlasenko70392332016-10-27 02:31:55 +020011253/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011254 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000011255 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011256
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011257#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000011258
Denys Vlasenko23841622015-10-09 15:52:03 +020011259/* Hash of mtimes of mailboxes */
11260static unsigned mailtime_hash;
Eric Andersenc470f442003-07-28 09:56:35 +000011261/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011262static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000011263
Eric Andersencb57d552001-06-28 07:25:16 +000011264/*
Eric Andersenc470f442003-07-28 09:56:35 +000011265 * Print appropriate message(s) if mail has arrived.
11266 * If mail_var_path_changed is set,
11267 * then the value of MAIL has mail_var_path_changed,
11268 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000011269 */
Eric Andersenc470f442003-07-28 09:56:35 +000011270static void
11271chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000011272{
Eric Andersencb57d552001-06-28 07:25:16 +000011273 const char *mpath;
11274 char *p;
11275 char *q;
Denys Vlasenko23841622015-10-09 15:52:03 +020011276 unsigned new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011277 struct stackmark smark;
11278 struct stat statb;
11279
Eric Andersencb57d552001-06-28 07:25:16 +000011280 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000011281 mpath = mpathset() ? mpathval() : mailval();
Denys Vlasenko23841622015-10-09 15:52:03 +020011282 new_hash = 0;
11283 for (;;) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011284 int len;
11285
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010011286 len = padvance_magic(&mpath, nullstr, 2);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011287 if (!len)
11288 break;
11289 p = stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011290 break;
11291 if (*p == '\0')
11292 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011293 for (q = p; *q; q++)
11294 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011295#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011296 if (q[-1] != '/')
11297 abort();
11298#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011299 q[-1] = '\0'; /* delete trailing '/' */
11300 if (stat(p, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011301 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000011302 }
Denys Vlasenko23841622015-10-09 15:52:03 +020011303 /* Very simplistic "hash": just a sum of all mtimes */
11304 new_hash += (unsigned)statb.st_mtime;
11305 }
11306 if (!mail_var_path_changed && mailtime_hash != new_hash) {
Denys Vlasenko4cd99e72015-10-09 16:02:53 +020011307 if (mailtime_hash != 0)
11308 out2str("you have mail\n");
Denys Vlasenko23841622015-10-09 15:52:03 +020011309 mailtime_hash = new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011310 }
Eric Andersenc470f442003-07-28 09:56:35 +000011311 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011312 popstackmark(&smark);
11313}
Eric Andersencb57d552001-06-28 07:25:16 +000011314
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011315static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011316changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011317{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011318 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011319}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011320
Denis Vlasenko131ae172007-02-18 13:00:19 +000011321#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000011322
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011323
11324/* ============ ??? */
11325
Eric Andersencb57d552001-06-28 07:25:16 +000011326/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011327 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000011328 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011329static void
11330setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011331{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011332 char **newparam;
11333 char **ap;
11334 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000011335
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011336 for (nparam = 0; argv[nparam]; nparam++)
11337 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011338 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
11339 while (*argv) {
11340 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000011341 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011342 *ap = NULL;
11343 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000011344 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011345 shellparam.nparam = nparam;
11346 shellparam.p = newparam;
11347#if ENABLE_ASH_GETOPTS
11348 shellparam.optind = 1;
11349 shellparam.optoff = -1;
11350#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011351}
11352
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011353/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011354 * Process shell options. The global variable argptr contains a pointer
11355 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011356 *
11357 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
11358 * For a non-interactive shell, an error condition encountered
11359 * by a special built-in ... shall cause the shell to write a diagnostic message
11360 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000011361 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011362 * ...
11363 * Utility syntax error (option or operand error) Shall exit
11364 * ...
11365 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
11366 * we see that bash does not do that (set "finishes" with error code 1 instead,
11367 * and shell continues), and people rely on this behavior!
11368 * Testcase:
11369 * set -o barfoo 2>/dev/null
11370 * echo $?
11371 *
11372 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011373 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011374static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011375plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000011376{
11377 int i;
11378
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011379 if (name) {
11380 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011381 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011382 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011383 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011384 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011385 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011386 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011387 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000011388 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011389 for (i = 0; i < NOPTS; i++) {
Denys Vlasenko2f9c1242019-08-02 16:43:36 +020011390 if (optnames(i)[0] == '\0')
11391 continue;
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011392 if (val) {
11393 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
11394 } else {
11395 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
11396 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011397 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011398 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011399}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011400static void
11401setoption(int flag, int val)
11402{
11403 int i;
11404
11405 for (i = 0; i < NOPTS; i++) {
Denys Vlasenkof3634582019-06-03 12:21:04 +020011406 if (optletters(i) == flag && optnames(i)[0] != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011407 optlist[i] = val;
11408 return;
11409 }
11410 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011411 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011412 /* NOTREACHED */
11413}
Denys Vlasenko897475a2019-06-01 16:35:09 +020011414/* If login_sh is not NULL, we are called to parse command line opts,
11415 * not "set -opts"
11416 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011417static int
Denys Vlasenko897475a2019-06-01 16:35:09 +020011418options(int *login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011419{
11420 char *p;
11421 int val;
11422 int c;
11423
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011424 if (login_sh != NULL) /* if we came from startup code */
Eric Andersencb57d552001-06-28 07:25:16 +000011425 minusc = NULL;
11426 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011427 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011428 if (c != '-' && c != '+')
11429 break;
11430 argptr++;
11431 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011432 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000011433 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011434 if (p[0] == '\0' || LONE_DASH(p)) {
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011435 if (login_sh == NULL) { /* we came from setcmd() */
Eric Andersen2870d962001-07-02 17:27:21 +000011436 /* "-" means turn off -x and -v */
11437 if (p[0] == '\0')
11438 xflag = vflag = 0;
11439 /* "--" means reset params */
11440 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011441 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000011442 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011443 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000011444 }
Eric Andersencb57d552001-06-28 07:25:16 +000011445 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011446 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000011447 while ((c = *p++) != '\0') {
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011448 if (login_sh != NULL) { /* if we came from startup code */
Denys Vlasenko897475a2019-06-01 16:35:09 +020011449 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
11450 if (c == 'c') {
11451 minusc = p; /* command is after shell args */
Denys Vlasenkof3634582019-06-03 12:21:04 +020011452 cflag = 1;
11453 continue;
11454 }
11455 if (c == 's') { /* -s, +s */
11456 sflag = 1;
11457 continue;
11458 }
11459 if (c == 'i') { /* -i, +i */
11460 iflag = 1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011461 continue;
11462 }
11463 if (c == 'l') {
11464 *login_sh = 1; /* -l or +l == --login */
11465 continue;
11466 }
11467 /* bash does not accept +-login, we also won't */
11468 if (val && (c == '-')) { /* long options */
11469 if (strcmp(p, "login") == 0) {
11470 *login_sh = 1;
11471 }
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011472/* TODO: --noprofile: e.g. if I want to run emergency shell from sulogin,
11473 * I want minimal/no shell init scripts - but it insists on running it as "-ash"...
11474 */
Denys Vlasenko897475a2019-06-01 16:35:09 +020011475 break;
11476 }
11477 }
11478 if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011479 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011480 /* it already printed err message */
11481 return 1; /* error */
11482 }
Eric Andersencb57d552001-06-28 07:25:16 +000011483 if (*argptr)
11484 argptr++;
11485 } else {
11486 setoption(c, val);
11487 }
11488 }
11489 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011490 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011491}
11492
Eric Andersencb57d552001-06-28 07:25:16 +000011493/*
Eric Andersencb57d552001-06-28 07:25:16 +000011494 * The shift builtin command.
11495 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011496static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011497shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011498{
11499 int n;
11500 char **ap1, **ap2;
11501
11502 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011503 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011504 n = number(argv[1]);
11505 if (n > shellparam.nparam)
Ingo van Lil9c8e94b2018-01-05 15:04:23 +010011506 return 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011507 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011508 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011509 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000011510 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011511 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000011512 }
11513 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011514 while ((*ap2++ = *ap1++) != NULL)
11515 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011516#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011517 shellparam.optind = 1;
11518 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000011519#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000011520 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011521 return 0;
11522}
11523
Eric Andersencb57d552001-06-28 07:25:16 +000011524/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011525 * POSIX requires that 'set' (but not export or readonly) output the
11526 * variables in lexicographic order - by the locale's collating order (sigh).
11527 * Maybe we could keep them in an ordered balanced binary tree
11528 * instead of hashed lists.
11529 * For now just roll 'em through qsort for printing...
11530 */
11531static int
11532showvars(const char *sep_prefix, int on, int off)
11533{
11534 const char *sep;
11535 char **ep, **epend;
11536
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010011537 ep = listvars(on, off, /*strlist:*/ NULL, &epend);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011538 qsort(ep, epend - ep, sizeof(char *), vpcmp);
11539
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011540 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011541
11542 for (; ep < epend; ep++) {
11543 const char *p;
11544 const char *q;
11545
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011546 p = endofname(*ep);
11547/* Used to have simple "p = strchrnul(*ep, '=')" here instead, but this
11548 * makes "export -p" to have output not suitable for "eval":
11549 * import os
11550 * os.environ["test-test"]="test"
11551 * if os.fork() == 0:
11552 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
11553 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ])
11554 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011555 q = nullstr;
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011556 if (*p == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011557 q = single_quote(++p);
11558 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
11559 }
11560 return 0;
11561}
11562
11563/*
Eric Andersencb57d552001-06-28 07:25:16 +000011564 * The set command builtin.
11565 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011566static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011567setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011568{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011569 int retval;
11570
Denis Vlasenko68404f12008-03-17 09:00:54 +000011571 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000011572 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011573
Denis Vlasenkob012b102007-02-19 22:43:01 +000011574 INT_OFF;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011575 retval = options(/*login_sh:*/ NULL);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011576 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011577 optschanged();
11578 if (*argptr != NULL) {
11579 setparam(argptr);
11580 }
Eric Andersencb57d552001-06-28 07:25:16 +000011581 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011582 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011583 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000011584}
11585
Denis Vlasenko131ae172007-02-18 13:00:19 +000011586#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011587static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011588change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000011589{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011590 uint32_t t;
11591
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011592 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000011593 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011594 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000011595 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020011596 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000011597 vrandom.flags &= ~VNOFUNC;
11598 } else {
11599 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011600 t = strtoul(value, NULL, 10);
11601 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000011602 }
Eric Andersenef02f822004-03-11 13:34:24 +000011603}
Eric Andersen16767e22004-03-16 05:14:10 +000011604#endif
11605
Ron Yorston1d371862019-04-15 10:52:05 +010011606#if BASH_EPOCH_VARS
11607static void FAST_FUNC
11608change_epoch(struct var *vepoch, const char *fmt)
11609{
11610 struct timeval tv;
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011611 char buffer[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
Ron Yorston1d371862019-04-15 10:52:05 +010011612
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011613 xgettimeofday(&tv);
11614 sprintf(buffer, fmt, (unsigned long long)tv.tv_sec, (unsigned)tv.tv_usec);
Ron Yorston1d371862019-04-15 10:52:05 +010011615 setvar(vepoch->var_text, buffer, VNOFUNC);
11616 vepoch->flags &= ~VNOFUNC;
11617}
11618
11619static void FAST_FUNC
11620change_seconds(const char *value UNUSED_PARAM)
11621{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011622 change_epoch(&vepochs, "%llu");
Ron Yorston1d371862019-04-15 10:52:05 +010011623}
11624
11625static void FAST_FUNC
11626change_realtime(const char *value UNUSED_PARAM)
11627{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011628 change_epoch(&vepochr, "%llu.%06u");
Ron Yorston1d371862019-04-15 10:52:05 +010011629}
11630#endif
11631
Denis Vlasenko131ae172007-02-18 13:00:19 +000011632#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011633static int
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011634getopts(char *optstr, char *optvar, char **optfirst)
Eric Andersencb57d552001-06-28 07:25:16 +000011635{
11636 char *p, *q;
11637 char c = '?';
11638 int done = 0;
Denys Vlasenko9c541002015-10-07 15:44:36 +020011639 char sbuf[2];
Eric Andersena48b0a32003-10-22 10:56:47 +000011640 char **optnext;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011641 int ind = shellparam.optind;
11642 int off = shellparam.optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000011643
Denys Vlasenko9c541002015-10-07 15:44:36 +020011644 sbuf[1] = '\0';
11645
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011646 shellparam.optind = -1;
11647 optnext = optfirst + ind - 1;
Eric Andersena48b0a32003-10-22 10:56:47 +000011648
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011649 if (ind <= 1 || off < 0 || (int)strlen(optnext[-1]) < off)
Eric Andersencb57d552001-06-28 07:25:16 +000011650 p = NULL;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011651 else
11652 p = optnext[-1] + off;
Eric Andersencb57d552001-06-28 07:25:16 +000011653 if (p == NULL || *p == '\0') {
11654 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000011655 p = *optnext;
11656 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011657 atend:
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011658 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011659 p = NULL;
11660 done = 1;
11661 goto out;
11662 }
11663 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011664 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000011665 goto atend;
11666 }
11667
11668 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000011669 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000011670 if (*q == '\0') {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011671 /* OPTERR is a bashism */
11672 const char *cp = lookupvar("OPTERR");
11673 if ((cp && LONE_CHAR(cp, '0'))
11674 || (optstr[0] == ':')
11675 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011676 sbuf[0] = c;
11677 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011678 setvar0("OPTARG", sbuf);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011679 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011680 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011681 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011682 }
11683 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000011684 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011685 }
11686 if (*++q == ':')
11687 q++;
11688 }
11689
11690 if (*++q == ':') {
11691 if (*p == '\0' && (p = *optnext) == NULL) {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011692 /* OPTERR is a bashism */
11693 const char *cp = lookupvar("OPTERR");
11694 if ((cp && LONE_CHAR(cp, '0'))
11695 || (optstr[0] == ':')
11696 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011697 sbuf[0] = c;
11698 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011699 setvar0("OPTARG", sbuf);
Eric Andersencb57d552001-06-28 07:25:16 +000011700 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011701 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011702 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011703 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011704 c = '?';
11705 }
Eric Andersenc470f442003-07-28 09:56:35 +000011706 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011707 }
11708
11709 if (p == *optnext)
11710 optnext++;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011711 setvar0("OPTARG", p);
Eric Andersencb57d552001-06-28 07:25:16 +000011712 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011713 } else
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011714 setvar0("OPTARG", nullstr);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011715 out:
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011716 ind = optnext - optfirst + 1;
11717 setvar("OPTIND", itoa(ind), VNOFUNC);
Denys Vlasenko9c541002015-10-07 15:44:36 +020011718 sbuf[0] = c;
11719 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011720 setvar0(optvar, sbuf);
11721
11722 shellparam.optoff = p ? p - *(optnext - 1) : -1;
11723 shellparam.optind = ind;
11724
Eric Andersencb57d552001-06-28 07:25:16 +000011725 return done;
11726}
Eric Andersenc470f442003-07-28 09:56:35 +000011727
11728/*
11729 * The getopts builtin. Shellparam.optnext points to the next argument
11730 * to be processed. Shellparam.optptr points to the next character to
11731 * be processed in the current argument. If shellparam.optnext is NULL,
11732 * then it's the first time getopts has been called.
11733 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011734static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000011735getoptscmd(int argc, char **argv)
11736{
11737 char **optbase;
11738
11739 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011740 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011741 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000011742 optbase = shellparam.p;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011743 if ((unsigned)shellparam.optind > shellparam.nparam + 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000011744 shellparam.optind = 1;
11745 shellparam.optoff = -1;
11746 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011747 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011748 optbase = &argv[3];
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011749 if ((unsigned)shellparam.optind > argc - 2) {
Eric Andersenc470f442003-07-28 09:56:35 +000011750 shellparam.optind = 1;
11751 shellparam.optoff = -1;
11752 }
11753 }
11754
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011755 return getopts(argv[1], argv[2], optbase);
Eric Andersenc470f442003-07-28 09:56:35 +000011756}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011757#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000011758
Eric Andersencb57d552001-06-28 07:25:16 +000011759
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011760/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000011761
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011762struct heredoc {
11763 struct heredoc *next; /* next here document in list */
11764 union node *here; /* redirection node */
11765 char *eofmark; /* string indicating end of input */
11766 smallint striptabs; /* if set, strip leading tabs */
11767};
11768
11769static smallint tokpushback; /* last token pushed back */
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011770static smallint quoteflag; /* set if (part of) last token was quoted */
11771static token_id_t lasttoken; /* last token read (integer id Txxx) */
11772static struct heredoc *heredoclist; /* list of here documents to read */
11773static char *wordtext; /* text of last word returned by readtoken */
11774static struct nodelist *backquotelist;
11775static union node *redirnode;
11776static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011777
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011778static const char *
11779tokname(char *buf, int tok)
11780{
11781 if (tok < TSEMI)
Denys Vlasenko888527c2016-10-02 16:54:17 +020011782 return tokname_array[tok];
11783 sprintf(buf, "\"%s\"", tokname_array[tok]);
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011784 return buf;
11785}
11786
11787/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011788 * Called when an unexpected token is read during the parse. The argument
11789 * is the token that is expected, or -1 if more than one type of token can
11790 * occur at this point.
11791 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011792static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011793static void
11794raise_error_unexpected_syntax(int token)
11795{
11796 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011797 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011798 int l;
11799
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011800 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011801 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011802 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011803 raise_error_syntax(msg);
11804 /* NOTREACHED */
11805}
Eric Andersencb57d552001-06-28 07:25:16 +000011806
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011807/* parsing is heavily cross-recursive, need these forward decls */
11808static union node *andor(void);
11809static union node *pipeline(void);
11810static union node *parse_command(void);
11811static void parseheredoc(void);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011812static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000011813
Eric Andersenc470f442003-07-28 09:56:35 +000011814static union node *
11815list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000011816{
Denys Vlasenko30af5932021-09-08 00:39:16 +020011817 int chknl = nlflag & 1 ? 0 : CHKNL;
Eric Andersencb57d552001-06-28 07:25:16 +000011818 union node *n1, *n2, *n3;
11819 int tok;
11820
Eric Andersencb57d552001-06-28 07:25:16 +000011821 n1 = NULL;
11822 for (;;) {
Denys Vlasenko30af5932021-09-08 00:39:16 +020011823 checkkwd = chknl | CHKKWD | CHKALIAS;
11824 tok = readtoken();
11825 switch (tok) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011826 case TNL:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011827 parseheredoc();
11828 return n1;
11829
11830 case TEOF:
Denys Vlasenko30af5932021-09-08 00:39:16 +020011831 if (!n1 && !chknl)
Ron Yorstonc0e00762015-10-29 11:30:55 +000011832 n1 = NODE_EOF;
Denys Vlasenko30af5932021-09-08 00:39:16 +020011833 out_eof:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011834 parseheredoc();
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011835 tokpushback++;
11836 lasttoken = TEOF;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011837 return n1;
11838 }
11839
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011840 tokpushback++;
Denys Vlasenko30af5932021-09-08 00:39:16 +020011841 if (nlflag == 2 && ((1 << tok) & tokendlist))
Ron Yorstonc0e00762015-10-29 11:30:55 +000011842 return n1;
11843 nlflag |= 2;
11844
Eric Andersencb57d552001-06-28 07:25:16 +000011845 n2 = andor();
11846 tok = readtoken();
11847 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000011848 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011849 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011850 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011851 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011852 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000011853 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011854 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011855 n2 = n3;
11856 }
11857 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000011858 }
11859 }
11860 if (n1 == NULL) {
11861 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011862 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011863 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011864 n3->type = NSEMI;
11865 n3->nbinary.ch1 = n1;
11866 n3->nbinary.ch2 = n2;
11867 n1 = n3;
11868 }
11869 switch (tok) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011870 case TEOF:
Denys Vlasenko30af5932021-09-08 00:39:16 +020011871 goto out_eof;
11872 case TNL:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011873 tokpushback = 1;
11874 /* fall through */
Eric Andersencb57d552001-06-28 07:25:16 +000011875 case TBACKGND:
11876 case TSEMI:
Eric Andersencb57d552001-06-28 07:25:16 +000011877 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011878 default:
Denys Vlasenko30af5932021-09-08 00:39:16 +020011879 if (!chknl)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011880 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011881 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011882 return n1;
11883 }
11884 }
11885}
11886
Eric Andersenc470f442003-07-28 09:56:35 +000011887static union node *
11888andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011889{
Eric Andersencb57d552001-06-28 07:25:16 +000011890 union node *n1, *n2, *n3;
11891 int t;
11892
Eric Andersencb57d552001-06-28 07:25:16 +000011893 n1 = pipeline();
11894 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011895 t = readtoken();
11896 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000011897 t = NAND;
11898 } else if (t == TOR) {
11899 t = NOR;
11900 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011901 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011902 return n1;
11903 }
Eric Andersenc470f442003-07-28 09:56:35 +000011904 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011905 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011906 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011907 n3->type = t;
11908 n3->nbinary.ch1 = n1;
11909 n3->nbinary.ch2 = n2;
11910 n1 = n3;
11911 }
11912}
11913
Eric Andersenc470f442003-07-28 09:56:35 +000011914static union node *
11915pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011916{
Eric Andersencb57d552001-06-28 07:25:16 +000011917 union node *n1, *n2, *pipenode;
11918 struct nodelist *lp, *prev;
11919 int negate;
11920
11921 negate = 0;
11922 TRACE(("pipeline: entered\n"));
11923 if (readtoken() == TNOT) {
11924 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000011925 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011926 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011927 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011928 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011929 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011930 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000011931 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011932 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011933 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000011934 pipenode->npipe.cmdlist = lp;
11935 lp->n = n1;
11936 do {
11937 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011938 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000011939 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011940 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011941 prev->next = lp;
11942 } while (readtoken() == TPIPE);
11943 lp->next = NULL;
11944 n1 = pipenode;
11945 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011946 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011947 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011948 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000011949 n2->type = NNOT;
11950 n2->nnot.com = n1;
11951 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011952 }
11953 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000011954}
11955
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011956static union node *
11957makename(void)
11958{
11959 union node *n;
11960
Denis Vlasenko597906c2008-02-20 16:38:54 +000011961 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011962 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011963 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011964 n->narg.text = wordtext;
11965 n->narg.backquote = backquotelist;
11966 return n;
11967}
11968
11969static void
11970fixredir(union node *n, const char *text, int err)
11971{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011972 int fd;
11973
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011974 TRACE(("Fix redir %s %d\n", text, err));
11975 if (!err)
11976 n->ndup.vname = NULL;
11977
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011978 fd = bb_strtou(text, NULL, 10);
11979 if (!errno && fd >= 0)
11980 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011981 else if (LONE_DASH(text))
11982 n->ndup.dupfd = -1;
11983 else {
11984 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011985 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011986 n->ndup.vname = makename();
11987 }
11988}
11989
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011990static void
11991parsefname(void)
11992{
11993 union node *n = redirnode;
11994
Denys Vlasenkoa7328982017-07-29 19:57:28 +020011995 if (n->type == NHERE)
11996 checkkwd = CHKEOFMARK;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011997 if (readtoken() != TWORD)
11998 raise_error_unexpected_syntax(-1);
11999 if (n->type == NHERE) {
12000 struct heredoc *here = heredoc;
12001 struct heredoc *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012002
12003 if (quoteflag == 0)
12004 n->type = NXHERE;
12005 TRACE(("Here document %d\n", n->type));
Denys Vlasenko740058b2018-01-09 17:01:00 +010012006 rmescapes(wordtext, 0, NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012007 here->eofmark = wordtext;
12008 here->next = NULL;
12009 if (heredoclist == NULL)
12010 heredoclist = here;
12011 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012012 for (p = heredoclist; p->next; p = p->next)
12013 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012014 p->next = here;
12015 }
12016 } else if (n->type == NTOFD || n->type == NFROMFD) {
12017 fixredir(n, wordtext, 0);
12018 } else {
12019 n->nfile.fname = makename();
12020 }
12021}
Eric Andersencb57d552001-06-28 07:25:16 +000012022
Eric Andersenc470f442003-07-28 09:56:35 +000012023static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012024simplecmd(void)
12025{
12026 union node *args, **app;
12027 union node *n = NULL;
12028 union node *vars, **vpp;
12029 union node **rpp, *redir;
12030 int savecheckkwd;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012031 int savelinno;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012032#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012033 smallint double_brackets_flag = 0;
12034#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012035 IF_BASH_FUNCTION(smallint function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012036
12037 args = NULL;
12038 app = &args;
12039 vars = NULL;
12040 vpp = &vars;
12041 redir = NULL;
12042 rpp = &redir;
12043
12044 savecheckkwd = CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012045 savelinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012046 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000012047 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012048 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000012049 t = readtoken();
12050 switch (t) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012051#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000012052 case TFUNCTION:
Denys Vlasenko30af5932021-09-08 00:39:16 +020012053 if (readtoken() != TWORD)
Ron Yorston95ebcf72015-11-03 09:42:23 +000012054 raise_error_unexpected_syntax(TWORD);
Denys Vlasenko30af5932021-09-08 00:39:16 +020012055 tokpushback = 1;
Ron Yorston95ebcf72015-11-03 09:42:23 +000012056 function_flag = 1;
12057 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012058#endif
12059#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012060 case TAND: /* "&&" */
12061 case TOR: /* "||" */
12062 if (!double_brackets_flag) {
12063 tokpushback = 1;
12064 goto out;
12065 }
Denys Vlasenkod2241f52020-10-31 03:34:07 +010012066 /* pass "&&" or "||" to [[ ]] as literal args */
12067 wordtext = (char *) (t == TAND ? "&&" : "||");
Denis Vlasenko80591b02008-03-25 07:49:43 +000012068#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012069 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012070 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012071 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012072 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012073 n->narg.text = wordtext;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012074#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012075 if (strcmp("[[", wordtext) == 0)
12076 double_brackets_flag = 1;
12077 else if (strcmp("]]", wordtext) == 0)
12078 double_brackets_flag = 0;
12079#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012080 n->narg.backquote = backquotelist;
12081 if (savecheckkwd && isassignment(wordtext)) {
12082 *vpp = n;
12083 vpp = &n->narg.next;
12084 } else {
12085 *app = n;
12086 app = &n->narg.next;
12087 savecheckkwd = 0;
12088 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012089#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000012090 if (function_flag) {
12091 checkkwd = CHKNL | CHKKWD;
Denys Vlasenko30af5932021-09-08 00:39:16 +020012092 t = readtoken();
12093 tokpushback = 1;
12094 switch (t) {
Ron Yorston95ebcf72015-11-03 09:42:23 +000012095 case TBEGIN:
12096 case TIF:
12097 case TCASE:
12098 case TUNTIL:
12099 case TWHILE:
12100 case TFOR:
12101 goto do_func;
12102 case TLP:
12103 function_flag = 0;
12104 break;
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020012105# if BASH_TEST2
Ron Yorston95ebcf72015-11-03 09:42:23 +000012106 case TWORD:
12107 if (strcmp("[[", wordtext) == 0)
12108 goto do_func;
12109 /* fall through */
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020012110# endif
Ron Yorston95ebcf72015-11-03 09:42:23 +000012111 default:
12112 raise_error_unexpected_syntax(-1);
12113 }
12114 }
12115#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012116 break;
12117 case TREDIR:
12118 *rpp = n = redirnode;
12119 rpp = &n->nfile.next;
12120 parsefname(); /* read name of redirection file */
12121 break;
12122 case TLP:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012123 IF_BASH_FUNCTION(do_func:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012124 if (args && app == &args->narg.next
12125 && !vars && !redir
12126 ) {
12127 struct builtincmd *bcmd;
12128 const char *name;
12129
12130 /* We have a function */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012131 if (IF_BASH_FUNCTION(!function_flag &&) readtoken() != TRP)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012132 raise_error_unexpected_syntax(TRP);
12133 name = n->narg.text;
12134 if (!goodname(name)
12135 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
12136 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012137 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012138 }
12139 n->type = NDEFUN;
12140 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012141 n->ndefun.text = n->narg.text;
12142 n->ndefun.linno = g_parsefile->linno;
12143 n->ndefun.body = parse_command();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012144 return n;
12145 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012146 IF_BASH_FUNCTION(function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012147 /* fall through */
12148 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012149 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012150 goto out;
12151 }
12152 }
12153 out:
12154 *app = NULL;
12155 *vpp = NULL;
12156 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012157 n = stzalloc(sizeof(struct ncmd));
Denys Vlasenko57b7efb2018-04-10 01:20:26 +020012158 if (NCMD != 0)
12159 n->type = NCMD;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012160 n->ncmd.linno = savelinno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012161 n->ncmd.args = args;
12162 n->ncmd.assign = vars;
12163 n->ncmd.redirect = redir;
12164 return n;
12165}
12166
12167static union node *
12168parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012169{
Eric Andersencb57d552001-06-28 07:25:16 +000012170 union node *n1, *n2;
12171 union node *ap, **app;
12172 union node *cp, **cpp;
12173 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000012174 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012175 int t;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012176 int savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012177
12178 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012179 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000012180
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012181 savelinno = g_parsefile->linno;
12182
Eric Andersencb57d552001-06-28 07:25:16 +000012183 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000012184 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012185 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000012186 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000012187 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012188 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000012189 n1->type = NIF;
12190 n1->nif.test = list(0);
12191 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012192 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000012193 n1->nif.ifpart = list(0);
12194 n2 = n1;
12195 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012196 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000012197 n2 = n2->nif.elsepart;
12198 n2->type = NIF;
12199 n2->nif.test = list(0);
12200 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012201 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000012202 n2->nif.ifpart = list(0);
12203 }
12204 if (lasttoken == TELSE)
12205 n2->nif.elsepart = list(0);
12206 else {
12207 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012208 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012209 }
Eric Andersenc470f442003-07-28 09:56:35 +000012210 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000012211 break;
12212 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000012213 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000012214 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012215 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012216 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000012217 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012218 got = readtoken();
12219 if (got != TDO) {
Denys Vlasenko888527c2016-10-02 16:54:17 +020012220 TRACE(("expecting DO got '%s' %s\n", tokname_array[got],
Denis Vlasenko131ae172007-02-18 13:00:19 +000012221 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012222 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012223 }
12224 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012225 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012226 break;
12227 }
12228 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000012229 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000012230 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012231 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000012232 n1->type = NFOR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012233 n1->nfor.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012234 n1->nfor.var = wordtext;
Ron Yorstonab80e012015-08-03 13:46:00 +010012235 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012236 if (readtoken() == TIN) {
12237 app = &ap;
12238 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012239 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012240 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012241 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012242 n2->narg.text = wordtext;
12243 n2->narg.backquote = backquotelist;
12244 *app = n2;
12245 app = &n2->narg.next;
12246 }
12247 *app = NULL;
12248 n1->nfor.args = ap;
12249 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012250 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000012251 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012252 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012253 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012254 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012255 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012256 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000012257 n1->nfor.args = n2;
12258 /*
12259 * Newline or semicolon here is optional (but note
12260 * that the original Bourne shell only allowed NL).
12261 */
Ron Yorstonab80e012015-08-03 13:46:00 +010012262 if (lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012263 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012264 }
Eric Andersenc470f442003-07-28 09:56:35 +000012265 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012266 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012267 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012268 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012269 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012270 break;
12271 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012272 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000012273 n1->type = NCASE;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012274 n1->ncase.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012275 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012276 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012277 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012278 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012279 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012280 n2->narg.text = wordtext;
12281 n2->narg.backquote = backquotelist;
Ron Yorston383b8852015-08-03 13:46:25 +010012282 checkkwd = CHKNL | CHKKWD | CHKALIAS;
12283 if (readtoken() != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012284 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000012285 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012286 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000012287 checkkwd = CHKNL | CHKKWD;
12288 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012289 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012290 if (lasttoken == TLP)
12291 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012292 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000012293 cp->type = NCLIST;
12294 app = &cp->nclist.pattern;
12295 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012296 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012297 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012298 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012299 ap->narg.text = wordtext;
12300 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000012301 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000012302 break;
12303 app = &ap->narg.next;
12304 readtoken();
12305 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000012306 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012307 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012308 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000012309 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000012310
Eric Andersenc470f442003-07-28 09:56:35 +000012311 cpp = &cp->nclist.next;
12312
12313 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012314 t = readtoken();
12315 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012316 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012317 raise_error_unexpected_syntax(TENDCASE);
12318 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000012319 }
Eric Andersenc470f442003-07-28 09:56:35 +000012320 }
Eric Andersencb57d552001-06-28 07:25:16 +000012321 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012322 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000012323 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012324 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012325 n1->type = NSUBSHELL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012326 n1->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012327 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012328 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012329 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000012330 break;
12331 case TBEGIN:
12332 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012333 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000012334 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012335 IF_BASH_FUNCTION(case TFUNCTION:)
Eric Andersencb57d552001-06-28 07:25:16 +000012336 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000012337 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012338 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012339 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000012340 }
12341
Eric Andersenc470f442003-07-28 09:56:35 +000012342 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012343 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000012344
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012345 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000012346 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000012347 checkkwd = CHKKWD | CHKALIAS;
12348 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012349 while (readtoken() == TREDIR) {
12350 *rpp = n2 = redirnode;
12351 rpp = &n2->nfile.next;
12352 parsefname();
12353 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012354 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012355 *rpp = NULL;
12356 if (redir) {
12357 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012358 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012359 n2->type = NREDIR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012360 n2->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012361 n2->nredir.n = n1;
12362 n1 = n2;
12363 }
12364 n1->nredir.redirect = redir;
12365 }
Eric Andersencb57d552001-06-28 07:25:16 +000012366 return n1;
12367}
12368
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012369#if BASH_DOLLAR_SQUOTE
Denys Vlasenko37dc08b2016-10-02 04:38:07 +020012370static int
12371decode_dollar_squote(void)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012372{
12373 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
12374 int c, cnt;
12375 char *p;
12376 char buf[4];
12377
12378 c = pgetc();
12379 p = strchr(C_escapes, c);
12380 if (p) {
12381 buf[0] = c;
12382 p = buf;
12383 cnt = 3;
12384 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
12385 do {
12386 c = pgetc();
12387 *++p = c;
12388 } while ((unsigned char)(c - '0') <= 7 && --cnt);
12389 pungetc();
12390 } else if (c == 'x') { /* \xHH */
12391 do {
12392 c = pgetc();
12393 *++p = c;
12394 } while (isxdigit(c) && --cnt);
12395 pungetc();
12396 if (cnt == 3) { /* \x but next char is "bad" */
12397 c = 'x';
12398 goto unrecognized;
12399 }
12400 } else { /* simple seq like \\ or \t */
12401 p++;
12402 }
12403 *p = '\0';
12404 p = buf;
12405 c = bb_process_escape_sequence((void*)&p);
12406 } else { /* unrecognized "\z": print both chars unless ' or " */
12407 if (c != '\'' && c != '"') {
12408 unrecognized:
12409 c |= 0x100; /* "please encode \, then me" */
12410 }
12411 }
12412 return c;
12413}
12414#endif
12415
Denys Vlasenko46999802017-07-29 21:12:29 +020012416/* Used by expandstr to get here-doc like behaviour. */
12417#define FAKEEOFMARK ((char*)(uintptr_t)1)
12418
12419static ALWAYS_INLINE int
12420realeofmark(const char *eofmark)
12421{
12422 return eofmark && eofmark != FAKEEOFMARK;
12423}
12424
Eric Andersencb57d552001-06-28 07:25:16 +000012425/*
12426 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
12427 * is not NULL, read a here document. In the latter case, eofmark is the
12428 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010012429 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000012430 * is the first character of the input token or document.
12431 *
12432 * Because C does not have internal subroutines, I have simulated them
12433 * using goto's to implement the subroutine linkage. The following macros
12434 * will run code that appears at the end of readtoken1.
12435 */
Eric Andersen2870d962001-07-02 17:27:21 +000012436#define CHECKEND() {goto checkend; checkend_return:;}
12437#define PARSEREDIR() {goto parseredir; parseredir_return:;}
12438#define PARSESUB() {goto parsesub; parsesub_return:;}
Ron Yorstona1b0d382020-07-23 08:32:27 +010012439#define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackq_oldreturn:;}
12440#define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackq_newreturn:;}
12441#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;}
Eric Andersen2870d962001-07-02 17:27:21 +000012442#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000012443static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010012444readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000012445{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012446 /* NB: syntax parameter fits into smallint */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012447 /* c parameter is an unsigned char or PEOF */
Eric Andersencb57d552001-06-28 07:25:16 +000012448 char *out;
Denys Vlasenko50e6d422016-09-30 11:35:54 +020012449 size_t len;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012450 struct nodelist *bqlist;
12451 smallint quotef;
Ron Yorstona1b0d382020-07-23 08:32:27 +010012452 smallint style;
12453 enum { OLD, NEW, PSUB };
12454#define oldstyle (style == OLD)
Denis Vlasenko46a53062007-09-24 18:30:02 +000012455 smallint pssyntax; /* we are expanding a prompt string */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012456 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012457 /* syntax stack */
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012458 struct synstack synbase = { };
Denys Vlasenko216913c2018-04-02 12:35:04 +020012459 struct synstack *synstack = &synbase;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012460
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012461#if ENABLE_ASH_EXPAND_PRMT
Denis Vlasenko46a53062007-09-24 18:30:02 +000012462 pssyntax = (syntax == PSSYNTAX);
12463 if (pssyntax)
12464 syntax = DQSYNTAX;
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012465#else
12466 pssyntax = 0; /* constant */
12467#endif
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012468 synstack->syntax = syntax;
12469
Denys Vlasenko216913c2018-04-02 12:35:04 +020012470 if (syntax == DQSYNTAX)
12471 synstack->dblquote = 1;
12472 quotef = 0;
12473 bqlist = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012474
12475 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012476 loop:
12477 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012478 CHECKEND(); /* set c to PEOF if at end of here document */
12479 for (;;) { /* until end of line or end of word */
12480 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012481 switch (SIT(c, synstack->syntax)) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012482 case CNL: /* '\n' */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012483 if (synstack->syntax == BASESYNTAX
12484 && !synstack->varnest
12485 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012486 goto endword; /* exit outer loop */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012487 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012488 USTPUTC(c, out);
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012489 nlprompt();
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012490 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012491 goto loop; /* continue outer loop */
12492 case CWORD:
12493 USTPUTC(c, out);
12494 break;
12495 case CCTL:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012496#if BASH_DOLLAR_SQUOTE
Denys Vlasenko958581a2010-09-12 15:04:27 +020012497 if (c == '\\' && bash_dollar_squote) {
12498 c = decode_dollar_squote();
Denys Vlasenko13f20912016-09-25 20:54:25 +020012499 if (c == '\0') {
12500 /* skip $'\000', $'\x00' (like bash) */
12501 break;
12502 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012503 if (c & 0x100) {
Denys Vlasenko13f20912016-09-25 20:54:25 +020012504 /* Unknown escape. Encode as '\z' */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012505 c = (unsigned char)c;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012506 if (eofmark == NULL || synstack->dblquote)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012507 USTPUTC(CTLESC, out);
12508 USTPUTC('\\', out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012509 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012510 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012511#endif
Denys Vlasenkoc4c20122018-04-02 13:29:20 +020012512 if (!eofmark || synstack->dblquote || synstack->varnest)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012513 USTPUTC(CTLESC, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012514 USTPUTC(c, out);
12515 break;
12516 case CBACK: /* backslash */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012517 c = pgetc();
Denys Vlasenko958581a2010-09-12 15:04:27 +020012518 if (c == PEOF) {
12519 USTPUTC(CTLESC, out);
12520 USTPUTC('\\', out);
12521 pungetc();
Denys Vlasenko958581a2010-09-12 15:04:27 +020012522 } else {
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012523 if (pssyntax && c == '$') {
Eric Andersenc470f442003-07-28 09:56:35 +000012524 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012525 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012526 }
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012527 /* Backslash is retained if we are in "str"
12528 * and next char isn't dquote-special.
12529 */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012530 if (synstack->dblquote
Denys Vlasenko958581a2010-09-12 15:04:27 +020012531 && c != '\\'
12532 && c != '`'
12533 && c != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020012534 && (c != '"' || (eofmark != NULL && !synstack->varnest))
12535 && (c != '}' || !synstack->varnest)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012536 ) {
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012537 USTPUTC(CTLESC, out); /* protect '\' from glob */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012538 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012539 }
Ron Yorston549deab2015-05-18 09:57:51 +020012540 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020012541 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012542 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012543 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012544 break;
12545 case CSQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012546 synstack->syntax = SQSYNTAX;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012547 quotemark:
12548 if (eofmark == NULL) {
12549 USTPUTC(CTLQUOTEMARK, out);
12550 }
12551 break;
12552 case CDQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012553 synstack->syntax = DQSYNTAX;
12554 synstack->dblquote = 1;
12555 toggledq:
12556 if (synstack->varnest)
12557 synstack->innerdq ^= 1;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012558 goto quotemark;
12559 case CENDQUOTE:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012560 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012561 if (eofmark != NULL && synstack->varnest == 0) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012562 USTPUTC(c, out);
Denys Vlasenko216913c2018-04-02 12:35:04 +020012563 break;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012564 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012565
12566 if (synstack->dqvarnest == 0) {
12567 synstack->syntax = BASESYNTAX;
12568 synstack->dblquote = 0;
12569 }
12570
12571 quotef = 1;
12572
12573 if (c == '"')
12574 goto toggledq;
12575
12576 goto quotemark;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012577 case CVAR: /* '$' */
12578 PARSESUB(); /* parse substitution */
12579 break;
12580 case CENDVAR: /* '}' */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012581 if (!synstack->innerdq && synstack->varnest > 0) {
12582 if (!--synstack->varnest && synstack->varpushed)
12583 synstack_pop(&synstack);
12584 else if (synstack->dqvarnest > 0)
12585 synstack->dqvarnest--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012586 c = CTLENDVAR;
12587 }
12588 USTPUTC(c, out);
12589 break;
Denys Vlasenko0b883582016-12-23 16:49:07 +010012590#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko958581a2010-09-12 15:04:27 +020012591 case CLP: /* '(' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012592 synstack->parenlevel++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012593 USTPUTC(c, out);
12594 break;
12595 case CRP: /* ')' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012596 if (synstack->parenlevel > 0) {
12597 synstack->parenlevel--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012598 } else {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012599 if (pgetc_eatbnl() == ')') {
Ron Yorstonad88bde2015-05-18 09:56:16 +020012600 c = CTLENDARI;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012601 synstack_pop(&synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012602 } else {
12603 /*
12604 * unbalanced parens
12605 * (don't 2nd guess - no error)
12606 */
12607 pungetc();
12608 }
12609 }
12610 USTPUTC(c, out);
12611 break;
12612#endif
12613 case CBQUOTE: /* '`' */
Denys Vlasenko41fddb42018-04-01 16:38:32 +020012614 if (checkkwd & CHKEOFMARK) {
12615 quotef = 1;
12616 USTPUTC('`', out);
12617 break;
12618 }
12619
Denys Vlasenko958581a2010-09-12 15:04:27 +020012620 PARSEBACKQOLD();
12621 break;
12622 case CENDFILE:
12623 goto endword; /* exit outer loop */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012624 default:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012625 if (synstack->varnest == 0) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012626#if BASH_REDIR_OUTPUT
Denys Vlasenko958581a2010-09-12 15:04:27 +020012627 if (c == '&') {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012628//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
Denys Vlasenko958581a2010-09-12 15:04:27 +020012629 if (pgetc() == '>')
12630 c = 0x100 + '>'; /* flag &> */
12631 pungetc();
12632 }
12633#endif
Ron Yorstona1b0d382020-07-23 08:32:27 +010012634#if BASH_PROCESS_SUBST
12635 if (c == '<' || c == '>') {
12636 if (pgetc() == '(') {
12637 PARSEPROCSUB();
12638 break;
12639 }
12640 pungetc();
12641 }
12642#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020012643 goto endword; /* exit outer loop */
12644 }
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012645 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012646 }
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012647 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012648 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012649 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020012650
Denys Vlasenko0b883582016-12-23 16:49:07 +010012651#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko216913c2018-04-02 12:35:04 +020012652 if (synstack->syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012653 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000012654#endif
Denys Vlasenko216913c2018-04-02 12:35:04 +020012655 if (synstack->syntax != BASESYNTAX && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012656 raise_error_syntax("unterminated quoted string");
Denys Vlasenko216913c2018-04-02 12:35:04 +020012657 if (synstack->varnest != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012658 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000012659 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000012660 }
12661 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012662 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000012663 out = stackblock();
12664 if (eofmark == NULL) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012665 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000012666 && quotef == 0
12667 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012668 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012669 PARSEREDIR(); /* passed as params: out, c */
12670 lasttoken = TREDIR;
12671 return lasttoken;
12672 }
12673 /* else: non-number X seen, interpret it
12674 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000012675 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012676 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012677 }
12678 quoteflag = quotef;
12679 backquotelist = bqlist;
12680 grabstackblock(len);
12681 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012682 lasttoken = TWORD;
12683 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000012684/* end of readtoken routine */
12685
Eric Andersencb57d552001-06-28 07:25:16 +000012686/*
12687 * Check to see whether we are at the end of the here document. When this
12688 * is called, c is set to the first character of the next input line. If
12689 * we are at the end of the here document, this routine sets the c to PEOF.
12690 */
Eric Andersenc470f442003-07-28 09:56:35 +000012691checkend: {
Denys Vlasenko46999802017-07-29 21:12:29 +020012692 if (realeofmark(eofmark)) {
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012693 int markloc;
12694 char *p;
12695
Eric Andersenc470f442003-07-28 09:56:35 +000012696 if (striptabs) {
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012697 while (c == '\t')
12698 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000012699 }
Eric Andersencb57d552001-06-28 07:25:16 +000012700
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012701 markloc = out - (char *)stackblock();
12702 for (p = eofmark; STPUTC(c, out), *p; p++) {
12703 if (c != *p)
12704 goto more_heredoc;
Denys Vlasenko35e349d2019-09-05 14:31:49 +020012705 /* FIXME: fails for backslash-newlined terminator:
12706 * cat <<EOF
12707 * ...
12708 * EO\
12709 * F
12710 * (see heredoc_bkslash_newline2.tests)
12711 */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012712 c = pgetc();
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012713 }
12714
12715 if (c == '\n' || c == PEOF) {
12716 c = PEOF;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020012717 if (trap_depth == 0)
12718 g_parsefile->linno++;
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012719 needprompt = doprompt;
12720 } else {
12721 int len_here;
12722
12723 more_heredoc:
12724 p = (char *)stackblock() + markloc + 1;
12725 len_here = out - p;
12726
12727 if (len_here) {
12728 len_here -= (c >= PEOF);
12729 c = p[-1];
12730
12731 if (len_here) {
12732 char *str;
12733
12734 str = alloca(len_here + 1);
12735 *(char *)mempcpy(str, p, len_here) = '\0';
12736
12737 pushstring(str, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000012738 }
12739 }
12740 }
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012741
12742 STADJUST((char *)stackblock() + markloc - out, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012743 }
Eric Andersenc470f442003-07-28 09:56:35 +000012744 goto checkend_return;
12745}
Eric Andersencb57d552001-06-28 07:25:16 +000012746
Eric Andersencb57d552001-06-28 07:25:16 +000012747/*
12748 * Parse a redirection operator. The variable "out" points to a string
12749 * specifying the fd to be redirected. The variable "c" contains the
12750 * first character of the redirection operator.
12751 */
Eric Andersenc470f442003-07-28 09:56:35 +000012752parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012753 /* out is already checked to be a valid number or "" */
12754 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000012755 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000012756
Denis Vlasenko597906c2008-02-20 16:38:54 +000012757 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000012758 if (c == '>') {
12759 np->nfile.fd = 1;
Denys Vlasenko220be532018-03-31 19:21:31 +020012760 c = pgetc_eatbnl();
Eric Andersenc470f442003-07-28 09:56:35 +000012761 if (c == '>')
12762 np->type = NAPPEND;
12763 else if (c == '|')
12764 np->type = NCLOBBER;
12765 else if (c == '&')
12766 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000012767 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000012768 else {
12769 np->type = NTO;
12770 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012771 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000012772 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012773#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000012774 else if (c == 0x100 + '>') { /* this flags &> redirection */
12775 np->nfile.fd = 1;
12776 pgetc(); /* this is '>', no need to check */
12777 np->type = NTO2;
12778 }
12779#endif
12780 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000012781 /*np->nfile.fd = 0; - stzalloc did it */
Denys Vlasenko220be532018-03-31 19:21:31 +020012782 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012783 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000012784 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012785 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012786 np = stzalloc(sizeof(struct nhere));
12787 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012788 }
12789 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012790 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000012791 heredoc->here = np;
Denys Vlasenko220be532018-03-31 19:21:31 +020012792 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012793 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000012794 heredoc->striptabs = 1;
12795 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012796 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012797 pungetc();
12798 }
12799 break;
12800
12801 case '&':
12802 np->type = NFROMFD;
12803 break;
12804
12805 case '>':
12806 np->type = NFROMTO;
12807 break;
12808
12809 default:
12810 np->type = NFROM;
12811 pungetc();
12812 break;
12813 }
Eric Andersencb57d552001-06-28 07:25:16 +000012814 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012815 if (fd >= 0)
12816 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000012817 redirnode = np;
12818 goto parseredir_return;
12819}
Eric Andersencb57d552001-06-28 07:25:16 +000012820
Eric Andersencb57d552001-06-28 07:25:16 +000012821/*
12822 * Parse a substitution. At this point, we have read the dollar sign
12823 * and nothing else.
12824 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012825
12826/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
12827 * (assuming ascii char codes, as the original implementation did) */
12828#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012829 (((unsigned)(c) - 33 < 32) \
12830 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000012831parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010012832 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012833 int typeloc;
Eric Andersencb57d552001-06-28 07:25:16 +000012834
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012835 c = pgetc_eatbnl();
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012836 if ((checkkwd & CHKEOFMARK)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012837 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000012838 ) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012839#if BASH_DOLLAR_SQUOTE
Denys Vlasenko216913c2018-04-02 12:35:04 +020012840 if (synstack->syntax != DQSYNTAX && c == '\'')
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012841 bash_dollar_squote = 1;
12842 else
12843#endif
12844 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012845 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012846 } else if (c == '(') {
12847 /* $(command) or $((arith)) */
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012848 if (pgetc_eatbnl() == '(') {
Denys Vlasenko0b883582016-12-23 16:49:07 +010012849#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000012850 PARSEARITH();
12851#else
Denys Vlasenko4f8079d2017-07-17 17:11:48 +020012852 raise_error_syntax("support for $((arith)) is disabled");
Eric Andersenc470f442003-07-28 09:56:35 +000012853#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012854 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000012855 pungetc();
12856 PARSEBACKQNEW();
12857 }
12858 } else {
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012859 /* $VAR, $<specialchar>, ${...}, or PEOF */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012860 smalluint newsyn = synstack->syntax;
12861
Eric Andersenc470f442003-07-28 09:56:35 +000012862 USTPUTC(CTLVAR, out);
12863 typeloc = out - (char *)stackblock();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012864 STADJUST(1, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012865 subtype = VSNORMAL;
12866 if (c == '{') {
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012867 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012868 subtype = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000012869 }
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012870 varname:
Denys Vlasenko3df14102016-10-26 16:41:13 +020012871 if (is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012872 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012873 do {
12874 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012875 c = pgetc_eatbnl();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012876 } while (is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012877 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012878 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012879 do {
12880 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012881 c = pgetc_eatbnl();
Denys Vlasenko53a7a9c2021-06-25 02:09:41 +020012882 } while ((subtype == 0 || subtype == VSLENGTH) && isdigit(c));
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012883 } else if (c != '}') {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012884 /* $[{[#]]<specialchar>[}] */
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012885 int cc = c;
12886
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012887 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012888 if (!subtype && cc == '#') {
12889 subtype = VSLENGTH;
12890 if (c == '_' || isalnum(c))
12891 goto varname;
12892 cc = c;
12893 c = pgetc_eatbnl();
12894 if (cc == '}' || c != '}') {
12895 pungetc();
12896 subtype = 0;
12897 c = cc;
12898 cc = '#';
12899 }
12900 }
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012901
12902 if (!is_special(cc)) {
12903 if (subtype == VSLENGTH)
12904 subtype = 0;
12905 goto badsub;
12906 }
12907
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012908 USTPUTC(cc, out);
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012909 } else
12910 goto badsub;
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012911
Eric Andersenc470f442003-07-28 09:56:35 +000012912 if (subtype == 0) {
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012913 static const char types[] ALIGN1 = "}-+?=";
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012914 /* ${VAR...} but not $VAR or ${#VAR} */
12915 /* c == first char after VAR */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012916 int cc = c;
12917
Eric Andersenc470f442003-07-28 09:56:35 +000012918 switch (c) {
12919 case ':':
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012920 c = pgetc_eatbnl();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012921#if BASH_SUBSTR
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012922 /* This check is only needed to not misinterpret
12923 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
12924 * constructs.
12925 */
12926 if (!strchr(types, c)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012927 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012928 pungetc();
Denys Vlasenko88e15702016-10-26 01:55:56 +020012929 break; /* "goto badsub" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012930 }
12931#endif
Denys Vlasenko3df14102016-10-26 16:41:13 +020012932 subtype = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000012933 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012934 default: {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012935 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000012936 if (p == NULL)
Denys Vlasenko88e15702016-10-26 01:55:56 +020012937 break;
Denys Vlasenko3df14102016-10-26 16:41:13 +020012938 subtype |= p - types + VSNORMAL;
Eric Andersenc470f442003-07-28 09:56:35 +000012939 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012940 }
Eric Andersenc470f442003-07-28 09:56:35 +000012941 case '%':
Denys Vlasenko216913c2018-04-02 12:35:04 +020012942 case '#':
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012943 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012944 c = pgetc_eatbnl();
Denys Vlasenko216913c2018-04-02 12:35:04 +020012945 if (c == cc)
12946 subtype++;
12947 else
12948 pungetc();
12949
12950 newsyn = BASESYNTAX;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012951 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012952#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012953 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020012954 /* ${v/[/]pattern/repl} */
12955//TODO: encode pattern and repl separately.
Denys Vlasenko216913c2018-04-02 12:35:04 +020012956// Currently cases like: v=1;echo ${v/$((1/1))/ONE}
12957// are broken (should print "ONE")
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012958 subtype = VSREPLACE;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012959 newsyn = BASESYNTAX;
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012960 c = pgetc_eatbnl();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012961 if (c != '/')
Denys Vlasenko88e15702016-10-26 01:55:56 +020012962 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012963 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012964 break;
12965#endif
Eric Andersencb57d552001-06-28 07:25:16 +000012966 }
Eric Andersenc470f442003-07-28 09:56:35 +000012967 } else {
Denys Vlasenko53a7a9c2021-06-25 02:09:41 +020012968 if (subtype == VSLENGTH && c != '}')
12969 subtype = 0;
Denys Vlasenko88e15702016-10-26 01:55:56 +020012970 badsub:
Eric Andersenc470f442003-07-28 09:56:35 +000012971 pungetc();
12972 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012973
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012974 if (newsyn == ARISYNTAX)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012975 newsyn = DQSYNTAX;
12976
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012977 if ((newsyn != synstack->syntax || synstack->innerdq)
12978 && subtype != VSNORMAL
12979 ) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012980 synstack_push(&synstack,
12981 synstack->prev ?: alloca(sizeof(*synstack)),
12982 newsyn);
12983
12984 synstack->varpushed = 1;
12985 synstack->dblquote = newsyn != BASESYNTAX;
12986 }
12987
Denys Vlasenko3df14102016-10-26 16:41:13 +020012988 ((unsigned char *)stackblock())[typeloc] = subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012989 if (subtype != VSNORMAL) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012990 synstack->varnest++;
12991 if (synstack->dblquote)
12992 synstack->dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000012993 }
Denys Vlasenko88e15702016-10-26 01:55:56 +020012994 STPUTC('=', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012995 }
Eric Andersenc470f442003-07-28 09:56:35 +000012996 goto parsesub_return;
12997}
Eric Andersencb57d552001-06-28 07:25:16 +000012998
Eric Andersencb57d552001-06-28 07:25:16 +000012999/*
13000 * Called to parse command substitutions. Newstyle is set if the command
13001 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
13002 * list of commands (passed by reference), and savelen is the number of
13003 * characters on the top of the stack which must be preserved.
13004 */
Eric Andersenc470f442003-07-28 09:56:35 +000013005parsebackq: {
13006 struct nodelist **nlpp;
Eric Andersenc470f442003-07-28 09:56:35 +000013007 union node *n;
Ron Yorston072fc602015-07-01 16:46:18 +010013008 char *str;
Eric Andersenc470f442003-07-28 09:56:35 +000013009 size_t savelen;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013010 struct heredoc *saveheredoclist;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013011 smallint saveprompt = 0;
13012
Eric Andersenc470f442003-07-28 09:56:35 +000013013 str = NULL;
13014 savelen = out - (char *)stackblock();
13015 if (savelen > 0) {
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020013016 /*
13017 * FIXME: this can allocate very large block on stack and SEGV.
13018 * Example:
13019 * echo "..<100kbytes>..`true` $(true) `true` ..."
Denys Vlasenko73737592016-09-17 20:58:22 +020013020 * allocates 100kb for every command subst. With about
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020013021 * a hundred command substitutions stack overflows.
13022 * With larger prepended string, SEGV happens sooner.
13023 */
Ron Yorston072fc602015-07-01 16:46:18 +010013024 str = alloca(savelen);
Eric Andersenc470f442003-07-28 09:56:35 +000013025 memcpy(str, stackblock(), savelen);
13026 }
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020013027
Eric Andersenc470f442003-07-28 09:56:35 +000013028 if (oldstyle) {
13029 /* We must read until the closing backquote, giving special
Denys Vlasenko60cb48c2013-01-14 15:57:44 +010013030 * treatment to some slashes, and then push the string and
13031 * reread it as input, interpreting it normally.
13032 */
Eric Andersenc470f442003-07-28 09:56:35 +000013033 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000013034 size_t psavelen;
13035 char *pstr;
13036
Eric Andersenc470f442003-07-28 09:56:35 +000013037 STARTSTACKSTR(pout);
13038 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020013039 int pc;
13040
13041 setprompt_if(needprompt, 2);
Denys Vlasenko220be532018-03-31 19:21:31 +020013042 pc = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013043 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000013044 case '`':
13045 goto done;
13046
13047 case '\\':
Denys Vlasenko777a6352020-09-29 16:25:32 +020013048 pc = pgetc(); /* not pgetc_eatbnl! */
Eric Andersenc470f442003-07-28 09:56:35 +000013049 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020013050 && (!synstack->dblquote || pc != '"')
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010013051 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000013052 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010013053 }
Denys Vlasenko48cb9832021-09-08 09:52:04 +020013054 break;
Eric Andersenc470f442003-07-28 09:56:35 +000013055
13056 case PEOF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013057 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000013058
13059 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013060 nlnoprompt();
Eric Andersenc470f442003-07-28 09:56:35 +000013061 break;
13062
13063 default:
13064 break;
13065 }
13066 STPUTC(pc, pout);
13067 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013068 done:
Eric Andersenc470f442003-07-28 09:56:35 +000013069 STPUTC('\0', pout);
13070 psavelen = pout - (char *)stackblock();
13071 if (psavelen > 0) {
13072 pstr = grabstackstr(pout);
13073 setinputstring(pstr);
13074 }
13075 }
13076 nlpp = &bqlist;
13077 while (*nlpp)
13078 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013079 *nlpp = stzalloc(sizeof(**nlpp));
13080 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000013081
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013082 saveheredoclist = heredoclist;
13083 heredoclist = NULL;
13084
Eric Andersenc470f442003-07-28 09:56:35 +000013085 if (oldstyle) {
13086 saveprompt = doprompt;
13087 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013088 }
13089
Eric Andersenc470f442003-07-28 09:56:35 +000013090 n = list(2);
13091
13092 if (oldstyle)
13093 doprompt = saveprompt;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013094 else {
13095 if (readtoken() != TRP)
13096 raise_error_unexpected_syntax(TRP);
13097 setinputstring(nullstr);
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013098 }
13099
Denys Vlasenko9a1a6592020-02-22 16:39:27 +010013100 parseheredoc();
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013101 heredoclist = saveheredoclist;
Eric Andersenc470f442003-07-28 09:56:35 +000013102
13103 (*nlpp)->n = n;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013104 /* Start reading from old file again. */
13105 popfile();
13106 /* Ignore any pushed back tokens left from the backquote parsing. */
13107 if (oldstyle)
Eric Andersenc470f442003-07-28 09:56:35 +000013108 tokpushback = 0;
Denys Vlasenkoc55847f2020-02-17 15:59:08 +010013109 out = growstackto(savelen + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000013110 if (str) {
13111 memcpy(out, str, savelen);
13112 STADJUST(savelen, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013113 }
Ron Yorstona1b0d382020-07-23 08:32:27 +010013114#if BASH_PROCESS_SUBST
13115 if (style == PSUB)
13116 USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out);
13117 else
13118#endif
13119 USTPUTC(CTLBACKQ, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013120 if (oldstyle)
13121 goto parsebackq_oldreturn;
Ron Yorstona1b0d382020-07-23 08:32:27 +010013122#if BASH_PROCESS_SUBST
13123 else if (style == PSUB)
13124 goto parsebackq_psreturn;
13125#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013126 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000013127}
13128
Denys Vlasenko0b883582016-12-23 16:49:07 +010013129#if ENABLE_FEATURE_SH_MATH
Eric Andersencb57d552001-06-28 07:25:16 +000013130/*
13131 * Parse an arithmetic expansion (indicate start of one and set state)
13132 */
Eric Andersenc470f442003-07-28 09:56:35 +000013133parsearith: {
Denys Vlasenko216913c2018-04-02 12:35:04 +020013134
13135 synstack_push(&synstack,
13136 synstack->prev ?: alloca(sizeof(*synstack)),
13137 ARISYNTAX);
13138 synstack->dblquote = 1;
Ron Yorstonad88bde2015-05-18 09:56:16 +020013139 USTPUTC(CTLARI, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013140 goto parsearith_return;
13141}
13142#endif
Eric Andersenc470f442003-07-28 09:56:35 +000013143} /* end of readtoken */
13144
Eric Andersencb57d552001-06-28 07:25:16 +000013145/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013146 * Read the next input token.
13147 * If the token is a word, we set backquotelist to the list of cmds in
13148 * backquotes. We set quoteflag to true if any part of the word was
13149 * quoted.
13150 * If the token is TREDIR, then we set redirnode to a structure containing
13151 * the redirection.
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013152 *
13153 * [Change comment: here documents and internal procedures]
13154 * [Readtoken shouldn't have any arguments. Perhaps we should make the
13155 * word parsing code into a separate routine. In this case, readtoken
13156 * doesn't need to have any internal procedures, but parseword does.
13157 * We could also make parseoperator in essence the main routine, and
13158 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000013159 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013160#define NEW_xxreadtoken
13161#ifdef NEW_xxreadtoken
13162/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013163static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000013164 '\n', '(', ')', /* singles */
13165 '&', '|', ';', /* doubles */
13166 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013167};
Eric Andersencb57d552001-06-28 07:25:16 +000013168
Denis Vlasenko834dee72008-10-07 09:18:30 +000013169#define xxreadtoken_singles 3
13170#define xxreadtoken_doubles 3
13171
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013172static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013173 TNL, TLP, TRP, /* only single occurrence allowed */
13174 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
13175 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013176 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013177};
13178
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013179static int
13180xxreadtoken(void)
13181{
13182 int c;
13183
13184 if (tokpushback) {
13185 tokpushback = 0;
13186 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000013187 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013188 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013189 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013190 c = pgetc_eatbnl();
Denys Vlasenko48cb9832021-09-08 09:52:04 +020013191 if (c == ' ' || c == '\t')
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013192 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013193
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013194 if (c == '#') {
13195 while ((c = pgetc()) != '\n' && c != PEOF)
13196 continue;
13197 pungetc();
13198 } else if (c == '\\') {
Denys Vlasenko220be532018-03-31 19:21:31 +020013199 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013200 } else {
13201 const char *p;
13202
13203 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
13204 if (c != PEOF) {
13205 if (c == '\n') {
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013206 nlnoprompt();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013207 }
13208
13209 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000013210 if (p == NULL)
13211 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013212
Denis Vlasenko834dee72008-10-07 09:18:30 +000013213 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denys Vlasenko1e5111b2018-04-01 03:04:55 +020013214 int cc = pgetc_eatbnl();
Denis Vlasenko834dee72008-10-07 09:18:30 +000013215 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013216 p += xxreadtoken_doubles + 1;
13217 } else {
13218 pungetc();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010013219#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000013220 if (c == '&' && cc == '>') /* &> */
13221 break; /* return readtoken1(...) */
13222#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013223 }
13224 }
13225 }
13226 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
13227 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013228 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013229 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000013230
13231 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013232}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013233#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013234#define RETURN(token) return lasttoken = token
13235static int
13236xxreadtoken(void)
13237{
13238 int c;
13239
13240 if (tokpushback) {
13241 tokpushback = 0;
13242 return lasttoken;
13243 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013244 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013245 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013246 c = pgetc_eatbnl();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013247 switch (c) {
13248 case ' ': case '\t':
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013249 continue;
13250 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000013251 while ((c = pgetc()) != '\n' && c != PEOF)
13252 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013253 pungetc();
13254 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013255 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013256 nlnoprompt();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013257 RETURN(TNL);
13258 case PEOF:
13259 RETURN(TEOF);
13260 case '&':
Denys Vlasenko220be532018-03-31 19:21:31 +020013261 if (pgetc_eatbnl() == '&')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013262 RETURN(TAND);
13263 pungetc();
13264 RETURN(TBACKGND);
13265 case '|':
Denys Vlasenko220be532018-03-31 19:21:31 +020013266 if (pgetc_eatbnl() == '|')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013267 RETURN(TOR);
13268 pungetc();
13269 RETURN(TPIPE);
13270 case ';':
Denys Vlasenko220be532018-03-31 19:21:31 +020013271 if (pgetc_eatbnl() == ';')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013272 RETURN(TENDCASE);
13273 pungetc();
13274 RETURN(TSEMI);
13275 case '(':
13276 RETURN(TLP);
13277 case ')':
13278 RETURN(TRP);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013279 }
Denys Vlasenko220be532018-03-31 19:21:31 +020013280 break;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013281 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013282 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
13283#undef RETURN
13284}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013285#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013286
13287static int
13288readtoken(void)
13289{
13290 int t;
Ron Yorston713f07d2015-10-29 16:44:56 +000013291 int kwd = checkkwd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013292#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013293 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013294#endif
13295
13296#if ENABLE_ASH_ALIAS
13297 top:
13298#endif
13299
13300 t = xxreadtoken();
13301
13302 /*
13303 * eat newlines
13304 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013305 if (kwd & CHKNL) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013306 while (t == TNL) {
13307 parseheredoc();
Denys Vlasenko8c68ae82021-09-08 01:43:12 +020013308 checkkwd = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013309 t = xxreadtoken();
13310 }
13311 }
13312
Denys Vlasenko8c68ae82021-09-08 01:43:12 +020013313 kwd |= checkkwd;
13314 checkkwd = 0;
13315
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013316 if (t != TWORD || quoteflag) {
13317 goto out;
13318 }
13319
13320 /*
13321 * check for keywords
13322 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013323 if (kwd & CHKKWD) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013324 const char *const *pp;
13325
13326 pp = findkwd(wordtext);
13327 if (pp) {
13328 lasttoken = t = pp - tokname_array;
Denys Vlasenko888527c2016-10-02 16:54:17 +020013329 TRACE(("keyword '%s' recognized\n", tokname_array[t]));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013330 goto out;
13331 }
13332 }
13333
Denys Vlasenko8c68ae82021-09-08 01:43:12 +020013334 if (kwd & CHKALIAS) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013335#if ENABLE_ASH_ALIAS
13336 struct alias *ap;
13337 ap = lookupalias(wordtext, 1);
13338 if (ap != NULL) {
13339 if (*ap->val) {
13340 pushstring(ap->val, ap);
13341 }
13342 goto top;
13343 }
13344#endif
13345 }
13346 out:
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013347#if DEBUG
13348 if (!alreadyseen)
Denys Vlasenko888527c2016-10-02 16:54:17 +020013349 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013350 else
Denys Vlasenko888527c2016-10-02 16:54:17 +020013351 TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013352#endif
13353 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000013354}
13355
Eric Andersencb57d552001-06-28 07:25:16 +000013356/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013357 * Read and parse a command. Returns NODE_EOF on end of file.
13358 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000013359 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013360static union node *
13361parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000013362{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013363 tokpushback = 0;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013364 checkkwd = 0;
13365 heredoclist = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013366 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013367 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013368 needprompt = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013369 return list(1);
13370}
13371
13372/*
13373 * Input any here documents.
13374 */
13375static void
13376parseheredoc(void)
13377{
13378 struct heredoc *here;
13379 union node *n;
13380
13381 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000013382 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013383
13384 while (here) {
Christoph Schulz03ad7ae2018-11-20 17:45:52 +010013385 tokpushback = 0;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013386 setprompt_if(needprompt, 2);
Denys Vlasenkoacf79f92020-02-14 16:12:06 +010013387 if (here->here->type == NHERE)
13388 readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs);
13389 else
13390 readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000013391 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013392 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013393 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013394 n->narg.text = wordtext;
13395 n->narg.backquote = backquotelist;
13396 here->here->nhere.doc = n;
13397 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000013398 }
Eric Andersencb57d552001-06-28 07:25:16 +000013399}
13400
13401
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013402static const char *
Denys Vlasenko46999802017-07-29 21:12:29 +020013403expandstr(const char *ps, int syntax_type)
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013404{
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013405 struct parsefile *file_stop;
13406 struct jmploc *volatile savehandler;
13407 struct heredoc *saveheredoclist;
13408 const char *result;
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013409 int saveprompt;
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013410 struct jmploc jmploc;
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013411 union node n;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013412 int err;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013413
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013414 file_stop = g_parsefile;
13415
Denys Vlasenko46999802017-07-29 21:12:29 +020013416 /* XXX Fix (char *) cast. */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013417 setinputstring((char *)ps);
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013418
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013419 saveheredoclist = heredoclist;
13420 heredoclist = NULL;
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013421 saveprompt = doprompt;
13422 doprompt = 0;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013423 result = ps;
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013424 savehandler = exception_handler;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013425 err = setjmp(jmploc.loc);
13426 if (err)
13427 goto out;
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013428
13429 /* readtoken1() might die horribly.
Denys Vlasenko3c183a82017-10-12 19:35:42 +020013430 * Try a prompt with syntactically wrong command:
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013431 * PS1='$(date "+%H:%M:%S) > '
13432 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013433 exception_handler = &jmploc;
Denys Vlasenkoc5402562021-09-08 01:03:57 +020013434 readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013435
13436 n.narg.type = NARG;
13437 n.narg.next = NULL;
13438 n.narg.text = wordtext;
13439 n.narg.backquote = backquotelist;
13440
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013441 /* expandarg() might fail too:
13442 * PS1='$((123+))'
13443 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013444 expandarg(&n, NULL, EXP_QUOTED);
13445 result = stackblock();
13446
13447out:
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013448 exception_handler = savehandler;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013449 if (err && exception_type != EXERROR)
13450 longjmp(exception_handler->loc, 1);
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013451
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013452 doprompt = saveprompt;
13453 /* Try: PS1='`xxx(`' */
13454 unwindfiles(file_stop);
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013455 heredoclist = saveheredoclist;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013456
13457 return result;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013458}
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013459
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013460static inline int
13461parser_eof(void)
13462{
13463 return tokpushback && lasttoken == TEOF;
13464}
13465
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013466/*
13467 * Execute a command or commands contained in a string.
13468 */
13469static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013470evalstring(char *s, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000013471{
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013472 struct jmploc *volatile savehandler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013473 struct jmploc jmploc;
13474 int ex;
13475
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013476 union node *n;
13477 struct stackmark smark;
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013478 int status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013479
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013480 s = sstrdup(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013481 setinputstring(s);
13482 setstackmark(&smark);
13483
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013484 status = 0;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013485 /* On exception inside execution loop, we must popfile().
13486 * Try interactively:
13487 * readonly a=a
13488 * command eval "a=b" # throws "is read only" error
13489 * "command BLTIN" is not supposed to abort (even in non-interactive use).
13490 * But if we skip popfile(), we hit EOF in eval's string, and exit.
13491 */
13492 savehandler = exception_handler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013493 ex = setjmp(jmploc.loc);
13494 if (ex)
13495 goto out;
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013496 exception_handler = &jmploc;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013497
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013498 while ((n = parsecmd(0)) != NODE_EOF) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013499 int i;
13500
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013501 i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013502 if (n)
13503 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013504 popstackmark(&smark);
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013505 if (evalskip)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013506 break;
13507 }
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013508 out:
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013509 popstackmark(&smark);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013510 popfile();
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013511 stunalloc(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013512
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013513 exception_handler = savehandler;
13514 if (ex)
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020013515 longjmp(exception_handler->loc, ex);
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013516
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013517 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000013518}
13519
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013520/*
13521 * The eval command.
13522 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013523static int FAST_FUNC
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013524evalcmd(int argc UNUSED_PARAM, char **argv, int flags)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013525{
13526 char *p;
13527 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013528
Denis Vlasenko68404f12008-03-17 09:00:54 +000013529 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013530 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013531 argv += 2;
13532 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013533 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013534 for (;;) {
13535 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013536 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013537 if (p == NULL)
13538 break;
13539 STPUTC(' ', concat);
13540 }
13541 STPUTC('\0', concat);
13542 p = grabstackstr(concat);
13543 }
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013544 return evalstring(p, flags & EV_TESTED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013545 }
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013546 return 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013547}
13548
13549/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010013550 * Read and execute commands.
13551 * "Top" is nonzero for the top level command loop;
13552 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013553 */
13554static int
13555cmdloop(int top)
13556{
13557 union node *n;
13558 struct stackmark smark;
13559 int inter;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013560 int status = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013561 int numeof = 0;
13562
13563 TRACE(("cmdloop(%d) called\n", top));
13564 for (;;) {
13565 int skip;
13566
13567 setstackmark(&smark);
13568#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000013569 if (doing_jobctl)
Denys Vlasenko9c541002015-10-07 15:44:36 +020013570 showjobs(SHOW_CHANGED|SHOW_STDERR);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013571#endif
13572 inter = 0;
13573 if (iflag && top) {
13574 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013575 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013576 }
13577 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020013578#if DEBUG
13579 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020013580 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000013581#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013582 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013583 if (!top || numeof >= 50)
13584 break;
13585 if (!stoppedjobs()) {
Ron Yorston5726df52021-09-12 11:21:48 +010013586 if (!iflag)
13587 break;
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013588 if (!Iflag) {
Ron Yorston5726df52021-09-12 11:21:48 +010013589 newline_and_flush(stderr);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013590 break;
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013591 }
Ron Yorston5726df52021-09-12 11:21:48 +010013592 /* "set -o ignoreeof" active, do not exit command loop on ^D */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013593 out2str("\nUse \"exit\" to leave shell.\n");
13594 }
13595 numeof++;
Denys Vlasenko41beb532021-09-07 01:52:21 +020013596 } else {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013597 int i;
13598
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000013599 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13600 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013601 numeof = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013602 i = evaltree(n, 0);
13603 if (n)
13604 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013605 }
13606 popstackmark(&smark);
13607 skip = evalskip;
13608
13609 if (skip) {
Denys Vlasenkocd24a502020-02-20 16:47:01 +010013610 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denys Vlasenko0840c912016-10-01 15:27:44 +020013611 break;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013612 }
13613 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013614 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013615}
13616
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013617/*
13618 * Take commands from a file. To be compatible we should do a path
13619 * search for the file, which is necessary to find sub-commands.
13620 */
13621static char *
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013622find_dot_file(char *basename)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013623{
13624 char *fullname;
13625 const char *path = pathval();
13626 struct stat statb;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013627 int len;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013628
13629 /* don't try this for absolute or relative paths */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013630 if (strchr(basename, '/'))
13631 return basename;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013632
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013633 while ((len = padvance(&path, basename)) >= 0) {
13634 fullname = stackblock();
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013635 if ((!pathopt || *pathopt == 'f')
13636 && !stat(fullname, &statb) && S_ISREG(statb.st_mode)
13637 ) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013638 /* This will be freed by the caller. */
13639 return stalloc(len);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013640 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013641 }
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013642 /* not found in PATH */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013643
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013644#if ENABLE_ASH_BASH_SOURCE_CURDIR
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013645 return basename;
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013646#else
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013647 ash_msg_and_raise_error("%s: not found", basename);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013648 /* NOTREACHED */
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013649#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013650}
13651
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013652static int FAST_FUNC
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013653dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013654{
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013655 /* "false; . empty_file; echo $?" should print 0, not 1: */
13656 int status = 0;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013657 char *fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013658 char **argv;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013659 char *args_need_save;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013660 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013661
Denys Vlasenko981a0562017-07-26 19:53:11 +020013662//???
13663// struct strlist *sp;
13664// for (sp = cmdenviron; sp; sp = sp->next)
13665// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013666
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013667 nextopt(nullstr); /* handle possible "--" */
13668 argv = argptr;
13669
13670 if (!argv[0]) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013671 /* bash says: "bash: .: filename argument required" */
13672 return 2; /* bash compat */
13673 }
13674
Denys Vlasenko091f8312013-03-17 14:25:22 +010013675 /* This aborts if file isn't found, which is POSIXly correct.
13676 * bash returns exitcode 1 instead.
13677 */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013678 fullname = find_dot_file(argv[0]);
13679 argv++;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013680 args_need_save = argv[0];
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010013681 if (args_need_save) { /* ". FILE ARGS", and ARGS are not empty */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013682 int argc;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013683 saveparam = shellparam;
13684 shellparam.malloced = 0;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013685 argc = 1;
13686 while (argv[argc])
13687 argc++;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013688 shellparam.nparam = argc;
13689 shellparam.p = argv;
13690 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013691
Denys Vlasenko091f8312013-03-17 14:25:22 +010013692 /* This aborts if file can't be opened, which is POSIXly correct.
13693 * bash returns exitcode 1 instead.
13694 */
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013695 setinputfile(fullname, INPUT_PUSH_FILE);
13696 commandname = fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013697 status = cmdloop(0);
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013698 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013699
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013700 if (args_need_save) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013701 freeparam(&shellparam);
13702 shellparam = saveparam;
13703 };
13704
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013705 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013706}
13707
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013708static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013709exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013710{
13711 if (stoppedjobs())
13712 return 0;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013713
Denys Vlasenko970470e2020-02-14 17:32:22 +010013714 if (argv[1])
13715 savestatus = number(argv[1]);
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013716
Denys Vlasenko704c5962021-09-15 19:31:44 +020013717//TODO: this script
13718// trap 'echo trap:$FUNCNAME' EXIT
13719// f() { exit; }
13720// f
13721//prints "trap:f" in bash. We can call exitshell() here to achieve this.
13722//For now, keeping dash code:
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013723 raise_exception(EXEXIT);
13724 /* NOTREACHED */
13725}
13726
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013727/*
13728 * Read a file containing shell functions.
13729 */
13730static void
13731readcmdfile(char *name)
13732{
13733 setinputfile(name, INPUT_PUSH_FILE);
13734 cmdloop(0);
13735 popfile();
13736}
13737
13738
Denis Vlasenkocc571512007-02-23 21:10:35 +000013739/* ============ find_command inplementation */
13740
13741/*
13742 * Resolve a command name. If you change this routine, you may have to
13743 * change the shellexec routine as well.
13744 */
13745static void
13746find_command(char *name, struct cmdentry *entry, int act, const char *path)
13747{
13748 struct tblentry *cmdp;
13749 int idx;
13750 int prev;
13751 char *fullname;
13752 struct stat statb;
13753 int e;
13754 int updatetbl;
13755 struct builtincmd *bcmd;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013756 int len;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013757
13758 /* If name contains a slash, don't use PATH or hash table */
13759 if (strchr(name, '/') != NULL) {
13760 entry->u.index = -1;
13761 if (act & DO_ABS) {
13762 while (stat(name, &statb) < 0) {
13763#ifdef SYSV
13764 if (errno == EINTR)
13765 continue;
13766#endif
13767 entry->cmdtype = CMDUNKNOWN;
13768 return;
13769 }
13770 }
13771 entry->cmdtype = CMDNORMAL;
13772 return;
13773 }
13774
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013775/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013776
13777 updatetbl = (path == pathval());
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013778 if (!updatetbl)
Denis Vlasenkocc571512007-02-23 21:10:35 +000013779 act |= DO_ALTPATH;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013780
13781 /* If name is in the table, check answer will be ok */
13782 cmdp = cmdlookup(name, 0);
13783 if (cmdp != NULL) {
13784 int bit;
13785
13786 switch (cmdp->cmdtype) {
13787 default:
13788#if DEBUG
13789 abort();
13790#endif
13791 case CMDNORMAL:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013792 bit = DO_ALTPATH | DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013793 break;
13794 case CMDFUNCTION:
13795 bit = DO_NOFUNC;
13796 break;
13797 case CMDBUILTIN:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013798 bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013799 break;
13800 }
13801 if (act & bit) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013802 if (act & bit & DO_REGBLTIN)
13803 goto fail;
13804
Denis Vlasenkocc571512007-02-23 21:10:35 +000013805 updatetbl = 0;
13806 cmdp = NULL;
13807 } else if (cmdp->rehash == 0)
13808 /* if not invalidated by cd, we're done */
13809 goto success;
13810 }
13811
13812 /* If %builtin not in path, check for builtin next */
13813 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013814 if (bcmd) {
13815 if (IS_BUILTIN_REGULAR(bcmd))
13816 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013817 if (act & DO_ALTPATH)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013818 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013819 if (builtinloc <= 0)
13820 goto builtin_success;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013821 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000013822
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013823 if (act & DO_REGBLTIN)
13824 goto fail;
13825
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013826#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013827 {
13828 int applet_no = find_applet_by_name(name);
13829 if (applet_no >= 0) {
13830 entry->cmdtype = CMDNORMAL;
13831 entry->u.index = -2 - applet_no;
13832 return;
13833 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013834 }
13835#endif
13836
Denis Vlasenkocc571512007-02-23 21:10:35 +000013837 /* We have to search path. */
13838 prev = -1; /* where to start */
13839 if (cmdp && cmdp->rehash) { /* doing a rehash */
13840 if (cmdp->cmdtype == CMDBUILTIN)
13841 prev = builtinloc;
13842 else
13843 prev = cmdp->param.index;
13844 }
13845
13846 e = ENOENT;
13847 idx = -1;
13848 loop:
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013849 while ((len = padvance(&path, name)) >= 0) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013850 const char *lpathopt = pathopt;
13851
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013852 fullname = stackblock();
Denis Vlasenkocc571512007-02-23 21:10:35 +000013853 idx++;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013854 if (lpathopt) {
13855 if (*lpathopt == 'b') {
Denis Vlasenkocc571512007-02-23 21:10:35 +000013856 if (bcmd)
13857 goto builtin_success;
13858 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013859 } else if (!(act & DO_NOFUNC)) {
13860 /* handled below */
13861 } else {
13862 /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013863 continue;
13864 }
13865 }
13866 /* if rehash, don't redo absolute path names */
13867 if (fullname[0] == '/' && idx <= prev) {
13868 if (idx < prev)
13869 continue;
13870 TRACE(("searchexec \"%s\": no change\n", name));
13871 goto success;
13872 }
13873 while (stat(fullname, &statb) < 0) {
13874#ifdef SYSV
13875 if (errno == EINTR)
13876 continue;
13877#endif
13878 if (errno != ENOENT && errno != ENOTDIR)
13879 e = errno;
13880 goto loop;
13881 }
13882 e = EACCES; /* if we fail, this will be the error */
13883 if (!S_ISREG(statb.st_mode))
13884 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013885 if (lpathopt) { /* this is a %func directory */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013886 stalloc(len);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000013887 /* NB: stalloc will return space pointed by fullname
13888 * (because we don't have any intervening allocations
13889 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013890 readcmdfile(fullname);
13891 cmdp = cmdlookup(name, 0);
13892 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
13893 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
13894 stunalloc(fullname);
13895 goto success;
13896 }
13897 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
13898 if (!updatetbl) {
13899 entry->cmdtype = CMDNORMAL;
13900 entry->u.index = idx;
13901 return;
13902 }
13903 INT_OFF;
13904 cmdp = cmdlookup(name, 1);
13905 cmdp->cmdtype = CMDNORMAL;
13906 cmdp->param.index = idx;
13907 INT_ON;
13908 goto success;
13909 }
13910
13911 /* We failed. If there was an entry for this command, delete it */
13912 if (cmdp && updatetbl)
13913 delete_cmd_entry();
William Pitcockd8fd88a2018-01-24 18:33:18 +010013914 if (act & DO_ERR) {
13915#if ENABLE_ASH_BASH_NOT_FOUND_HOOK
13916 struct tblentry *hookp = cmdlookup("command_not_found_handle", 0);
13917 if (hookp && hookp->cmdtype == CMDFUNCTION) {
13918 char *argv[3];
13919 argv[0] = (char*) "command_not_found_handle";
13920 argv[1] = name;
13921 argv[2] = NULL;
13922 evalfun(hookp->param.func, 2, argv, 0);
13923 entry->cmdtype = CMDUNKNOWN;
13924 return;
13925 }
13926#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000013927 ash_msg("%s: %s", name, errmsg(e, "not found"));
William Pitcockd8fd88a2018-01-24 18:33:18 +010013928 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013929 fail:
Denis Vlasenkocc571512007-02-23 21:10:35 +000013930 entry->cmdtype = CMDUNKNOWN;
13931 return;
13932
13933 builtin_success:
13934 if (!updatetbl) {
13935 entry->cmdtype = CMDBUILTIN;
13936 entry->u.cmd = bcmd;
13937 return;
13938 }
13939 INT_OFF;
13940 cmdp = cmdlookup(name, 1);
13941 cmdp->cmdtype = CMDBUILTIN;
13942 cmdp->param.cmd = bcmd;
13943 INT_ON;
13944 success:
13945 cmdp->rehash = 0;
13946 entry->cmdtype = cmdp->cmdtype;
13947 entry->u = cmdp->param;
13948}
13949
13950
Eric Andersencb57d552001-06-28 07:25:16 +000013951/*
Eric Andersencb57d552001-06-28 07:25:16 +000013952 * The trap builtin.
13953 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013954static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013955trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000013956{
13957 char *action;
13958 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013959 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013960
Eric Andersenc470f442003-07-28 09:56:35 +000013961 nextopt(nullstr);
13962 ap = argptr;
13963 if (!*ap) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013964 for (signo = 0; signo <= NTRAP_LAST; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013965 char *tr = trap_ptr[signo];
13966 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013967 /* note: bash adds "SIG", but only if invoked
13968 * as "bash". If called as "sh", or if set -o posix,
13969 * then it prints short signal names.
13970 * We are printing short names: */
13971 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013972 single_quote(tr),
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013973 (signo == NTRAP_ERR) ? "ERR" : get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013974 /* trap_ptr != trap only if we are in special-cased `trap` code.
13975 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013976 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013977 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000013978 }
13979 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013980 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013981 if (trap_ptr != trap) {
13982 free(trap_ptr);
13983 trap_ptr = trap;
13984 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013985 */
Eric Andersencb57d552001-06-28 07:25:16 +000013986 return 0;
13987 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013988
Denys Vlasenko86981e32017-07-25 20:06:17 +020013989 /* Why the second check?
13990 * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..."
13991 * In this case, NUM is signal no, not an action.
13992 */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000013993 action = NULL;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013994 if (ap[1] && !is_number(ap[0]))
Eric Andersencb57d552001-06-28 07:25:16 +000013995 action = *ap++;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013996
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013997 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013998 while (*ap) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013999 signo = strcmp(*ap, "ERR") == 0 ? NTRAP_ERR : get_signum(*ap);
Denys Vlasenko86981e32017-07-25 20:06:17 +020014000 if (signo < 0) {
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010014001 /* Mimic bash message exactly */
14002 ash_msg("%s: invalid signal specification", *ap);
14003 exitcode = 1;
14004 goto next;
14005 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000014006 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000014007 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000014008 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000014009 action = NULL;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020014010 else {
14011 if (action[0]) /* not NULL and not "" and not "-" */
14012 may_have_traps = 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000014013 action = ckstrdup(action);
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020014014 }
Eric Andersencb57d552001-06-28 07:25:16 +000014015 }
Denis Vlasenko60818682007-09-28 22:07:23 +000014016 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000014017 trap[signo] = action;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014018 if (signo != 0 && signo < NSIG)
Eric Andersencb57d552001-06-28 07:25:16 +000014019 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000014020 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010014021 next:
Eric Andersencb57d552001-06-28 07:25:16 +000014022 ap++;
14023 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010014024 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000014025}
14026
Eric Andersenc470f442003-07-28 09:56:35 +000014027
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014028/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000014029
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014030#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014031static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014032helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014033{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000014034 unsigned col;
14035 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000014036
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020014037 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000014038 "Built-in commands:\n"
14039 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000014040 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000014041 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000014042 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014043 if (col > 60) {
14044 out1fmt("\n");
14045 col = 0;
14046 }
14047 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014048# if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000014049 {
14050 const char *a = applet_names;
14051 while (*a) {
14052 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14053 if (col > 60) {
14054 out1fmt("\n");
14055 col = 0;
14056 }
Ron Yorston2b919582016-04-08 11:57:20 +010014057 while (*a++ != '\0')
14058 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000014059 }
14060 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014061# endif
Denys Vlasenkoebedb942016-10-02 18:45:09 +020014062 newline_and_flush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000014063 return EXIT_SUCCESS;
14064}
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014065#endif
Eric Andersenc470f442003-07-28 09:56:35 +000014066
Flemming Madsend96ffda2013-04-07 18:47:24 +020014067#if MAX_HISTORY
14068static int FAST_FUNC
14069historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14070{
Ron Yorston9f3b4102019-12-16 09:31:10 +000014071 show_history(line_input_state);
Flemming Madsend96ffda2013-04-07 18:47:24 +020014072 return EXIT_SUCCESS;
14073}
14074#endif
14075
Eric Andersencb57d552001-06-28 07:25:16 +000014076/*
Eric Andersencb57d552001-06-28 07:25:16 +000014077 * The export and readonly commands.
14078 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014079static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014080exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000014081{
14082 struct var *vp;
14083 char *name;
14084 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000014085 char **aptr;
Denys Vlasenkod5275882012-10-01 13:41:17 +020014086 char opt;
14087 int flag;
14088 int flag_off;
Eric Andersencb57d552001-06-28 07:25:16 +000014089
Denys Vlasenkod5275882012-10-01 13:41:17 +020014090 /* "readonly" in bash accepts, but ignores -n.
14091 * We do the same: it saves a conditional in nextopt's param.
14092 */
14093 flag_off = 0;
14094 while ((opt = nextopt("np")) != '\0') {
14095 if (opt == 'n')
14096 flag_off = VEXPORT;
14097 }
14098 flag = VEXPORT;
14099 if (argv[0][0] == 'r') {
14100 flag = VREADONLY;
14101 flag_off = 0; /* readonly ignores -n */
14102 }
14103 flag_off = ~flag_off;
14104
Denys Vlasenko10ad6222017-04-17 16:13:32 +020014105 /*if (opt_p_not_specified) - bash doesn't check this. Try "export -p NAME" */
Denys Vlasenkod5275882012-10-01 13:41:17 +020014106 {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014107 aptr = argptr;
14108 name = *aptr;
14109 if (name) {
14110 do {
14111 p = strchr(name, '=');
14112 if (p != NULL) {
14113 p++;
14114 } else {
14115 vp = *findvar(hashvar(name), name);
14116 if (vp) {
Denys Vlasenkod5275882012-10-01 13:41:17 +020014117 vp->flags = ((vp->flags | flag) & flag_off);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014118 continue;
14119 }
Eric Andersencb57d552001-06-28 07:25:16 +000014120 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020014121 setvar(name, p, (flag & flag_off));
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014122 } while ((name = *++aptr) != NULL);
14123 return 0;
14124 }
Eric Andersencb57d552001-06-28 07:25:16 +000014125 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020014126
14127 /* No arguments. Show the list of exported or readonly vars.
14128 * -n is ignored.
14129 */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014130 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000014131 return 0;
14132}
14133
Eric Andersencb57d552001-06-28 07:25:16 +000014134/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014135 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000014136 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014137static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014138unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000014139{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014140 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000014141
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014142 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014143 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014144 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000014145}
14146
Eric Andersencb57d552001-06-28 07:25:16 +000014147/*
Eric Andersencb57d552001-06-28 07:25:16 +000014148 * The unset builtin command. We unset the function before we unset the
14149 * variable to allow a function to be unset when there is a readonly variable
14150 * with the same name.
14151 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014152static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014153unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000014154{
14155 char **ap;
14156 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014157 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000014158
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014159 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000014160 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000014161 }
Eric Andersencb57d552001-06-28 07:25:16 +000014162
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014163 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000014164 if (flag != 'f') {
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020014165 unsetvar(*ap);
14166 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000014167 }
14168 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000014169 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000014170 }
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020014171 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000014172}
14173
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000014174static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014175 ' ', offsetof(struct tms, tms_utime),
14176 '\n', offsetof(struct tms, tms_stime),
14177 ' ', offsetof(struct tms, tms_cutime),
14178 '\n', offsetof(struct tms, tms_cstime),
14179 0
14180};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014181static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014182timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014183{
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014184 unsigned clk_tck;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014185 const unsigned char *p;
14186 struct tms buf;
14187
Bartosz Golaszewski5d2e4092014-06-22 14:01:13 +020014188 clk_tck = bb_clk_tck();
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014189
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014190 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014191 p = timescmd_str;
14192 do {
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014193 unsigned sec, frac;
14194 unsigned long t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014195 t = *(clock_t *)(((char *) &buf) + p[1]);
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014196 sec = t / clk_tck;
14197 frac = t % clk_tck;
14198 out1fmt("%um%u.%03us%c",
14199 sec / 60, sec % 60,
14200 (frac * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014201 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014202 p += 2;
14203 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014204
Eric Andersencb57d552001-06-28 07:25:16 +000014205 return 0;
14206}
14207
Denys Vlasenko0b883582016-12-23 16:49:07 +010014208#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000014209/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014210 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014211 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000014212 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014213 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000014214 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014215static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014216letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014217{
Denis Vlasenko68404f12008-03-17 09:00:54 +000014218 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000014219
Denis Vlasenko68404f12008-03-17 09:00:54 +000014220 argv++;
14221 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000014222 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000014223 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014224 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000014225 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014226
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000014227 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000014228}
Eric Andersenc470f442003-07-28 09:56:35 +000014229#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000014230
Eric Andersenc470f442003-07-28 09:56:35 +000014231/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014232 * The read builtin. Options:
14233 * -r Do not interpret '\' specially
14234 * -s Turn off echo (tty only)
14235 * -n NCHARS Read NCHARS max
14236 * -p PROMPT Display PROMPT on stderr (if input is from tty)
14237 * -t SECONDS Timeout after SECONDS (tty or pipe only)
14238 * -u FD Read from given FD instead of fd 0
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014239 * -d DELIM End on DELIM char, not newline
Eric Andersenc470f442003-07-28 09:56:35 +000014240 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014241 * TODO: bash also has:
14242 * -a ARRAY Read into array[0],[1],etc
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014243 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000014244 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014245static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014246readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014247{
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014248 struct builtin_read_params params;
Denys Vlasenko73067272010-01-12 22:11:24 +010014249 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000014250 int i;
14251
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014252 memset(&params, 0, sizeof(params));
14253
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014254 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000014255 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000014256 case 'p':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014257 params.opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014258 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014259 case 'n':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014260 params.opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014261 break;
14262 case 's':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014263 params.read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000014264 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014265 case 't':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014266 params.opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014267 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014268 case 'r':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014269 params.read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000014270 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014271 case 'u':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014272 params.opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014273 break;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014274#if BASH_READ_D
14275 case 'd':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014276 params.opt_d = optionarg;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014277 break;
14278#endif
Paul Fox02eb9342005-09-07 16:56:02 +000014279 default:
14280 break;
14281 }
Eric Andersenc470f442003-07-28 09:56:35 +000014282 }
Paul Fox02eb9342005-09-07 16:56:02 +000014283
Denys Vlasenko457825f2021-06-06 12:07:11 +020014284 if (!ENABLE_ASH_BASH_COMPAT && !argptr) {
14285 bb_simple_error_msg("read: need variable name");
14286 return 1;
14287 }
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014288 params.argv = argptr;
14289 params.setvar = setvar0;
14290 params.ifs = bltinlookup("IFS"); /* can be NULL */
14291
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014292 /* "read -s" needs to save/restore termios, can't allow ^C
14293 * to jump out of it.
14294 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014295 again:
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014296 INT_OFF;
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014297 r = shell_builtin_read(&params);
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014298 INT_ON;
Denis Vlasenko46aeab92009-03-31 19:18:17 +000014299
Denys Vlasenkof5470412017-05-22 19:34:45 +020014300 if ((uintptr_t)r == 1 && errno == EINTR) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014301 /* To get SIGCHLD: sleep 1 & read x; echo $x
14302 * Correct behavior is to not exit "read"
14303 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014304 if (pending_sig == 0)
14305 goto again;
14306 }
14307
Denys Vlasenko73067272010-01-12 22:11:24 +010014308 if ((uintptr_t)r > 1)
14309 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000014310
Denys Vlasenko73067272010-01-12 22:11:24 +010014311 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000014312}
14313
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014314static int FAST_FUNC
Denys Vlasenko6283f982015-10-07 16:56:20 +020014315umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014316{
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014317 static const char permuser[3] ALIGN1 = "ogu";
Eric Andersenc470f442003-07-28 09:56:35 +000014318
Eric Andersenc470f442003-07-28 09:56:35 +000014319 mode_t mask;
Eric Andersenc470f442003-07-28 09:56:35 +000014320 int symbolic_mode = 0;
14321
14322 while (nextopt("S") != '\0') {
14323 symbolic_mode = 1;
14324 }
14325
Denis Vlasenkob012b102007-02-19 22:43:01 +000014326 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000014327 mask = umask(0);
14328 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000014329 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000014330
Denys Vlasenko6283f982015-10-07 16:56:20 +020014331 if (*argptr == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000014332 if (symbolic_mode) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014333 char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
Eric Andersenc470f442003-07-28 09:56:35 +000014334 char *p = buf;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014335 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014336
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014337 i = 2;
14338 for (;;) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014339 *p++ = ',';
Eric Andersenc470f442003-07-28 09:56:35 +000014340 *p++ = permuser[i];
14341 *p++ = '=';
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014342 /* mask is 0..0uuugggooo. i=2 selects uuu bits */
Denys Vlasenko005c4922015-10-10 20:17:12 +020014343 if (!(mask & 0400)) *p++ = 'r';
14344 if (!(mask & 0200)) *p++ = 'w';
14345 if (!(mask & 0100)) *p++ = 'x';
14346 mask <<= 3;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014347 if (--i < 0)
14348 break;
Eric Andersenc470f442003-07-28 09:56:35 +000014349 }
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014350 *p = '\0';
Denys Vlasenko005c4922015-10-10 20:17:12 +020014351 puts(buf + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014352 } else {
Denys Vlasenkoec046f72015-10-07 17:57:53 +020014353 out1fmt("%04o\n", mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014354 }
14355 } else {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014356 char *modestr = *argptr;
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020014357 /* numeric umasks are taken as-is */
14358 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
Denys Vlasenko6283f982015-10-07 16:56:20 +020014359 if (!isdigit(modestr[0]))
14360 mask ^= 0777;
Denys Vlasenko5711a2a2015-10-07 17:55:33 +020014361 mask = bb_parse_mode(modestr, mask);
14362 if ((unsigned)mask > 0777) {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014363 ash_msg_and_raise_error("illegal mode: %s", modestr);
Eric Andersenc470f442003-07-28 09:56:35 +000014364 }
Denys Vlasenko6283f982015-10-07 16:56:20 +020014365 if (!isdigit(modestr[0]))
14366 mask ^= 0777;
14367 umask(mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014368 }
14369 return 0;
14370}
14371
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014372static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014373ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014374{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014375 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014376}
14377
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014378/* ============ main() and helpers */
14379
14380/*
Denys Vlasenkof977e002020-02-20 16:54:29 +010014381 * This routine is called when an error or an interrupt occurs in an
14382 * interactive shell and control is returned to the main command loop
14383 * but prior to exitshell.
14384 */
14385static void
14386exitreset(void)
14387{
14388 /* from eval.c: */
14389 if (savestatus >= 0) {
14390 if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
14391 exitstatus = savestatus;
14392 savestatus = -1;
14393 }
14394 evalskip = 0;
14395 loopnest = 0;
Denys Vlasenkoeb607772021-09-09 16:26:41 +020014396 inps4 = 0;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014397
14398 /* from expand.c: */
14399 ifsfree();
14400
14401 /* from redir.c: */
14402 unwindredir(NULL);
14403}
14404
14405/*
14406 * This routine is called when an error or an interrupt occurs in an
14407 * interactive shell and control is returned to the main command loop.
14408 * (In dash, this function is auto-generated by build machinery).
14409 */
14410static void
14411reset(void)
14412{
14413 /* from input.c: */
14414 g_parsefile->left_in_buffer = 0;
14415 g_parsefile->left_in_line = 0; /* clear input buffer */
Denys Vlasenko51a471d2020-12-24 00:22:24 +010014416 g_parsefile->unget = 0;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014417 popallfiles();
14418
14419 /* from var.c: */
14420 unwindlocalvars(NULL);
14421}
14422
14423/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014424 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014425 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014426static void
14427exitshell(void)
14428{
14429 struct jmploc loc;
14430 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014431
Denys Vlasenkobede2152011-09-04 16:12:33 +020014432#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
Denys Vlasenko00eb23b2020-12-21 21:36:58 +010014433 save_history(line_input_state); /* may be NULL */
Denys Vlasenkobede2152011-09-04 16:12:33 +020014434#endif
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010014435 savestatus = exitstatus;
14436 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
14437 if (setjmp(loc.loc))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014438 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014439 exception_handler = &loc;
14440 p = trap[0];
14441 if (p) {
14442 trap[0] = NULL;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020014443 evalskip = 0;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014444 trap_depth++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014445 evalstring(p, 0);
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014446 trap_depth--;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014447 evalskip = SKIPFUNCDEF;
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014448 /*free(p); - we'll exit soon */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014449 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014450 out:
Denys Vlasenkof977e002020-02-20 16:54:29 +010014451 exitreset();
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014452 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14453 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
14454 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014455 setjobctl(0);
Denys Vlasenkocaee80c2016-10-25 20:49:53 +020014456 flush_stdout_stderr();
Denys Vlasenkof977e002020-02-20 16:54:29 +010014457 _exit(exitstatus);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014458 /* NOTREACHED */
14459}
14460
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014461/* Don't inline: conserve stack of caller from having our locals too */
14462static NOINLINE void
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014463init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014464{
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020014465 /* we will never free this */
14466 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denys Vlasenko0485b672017-08-14 19:46:56 +020014467 basepf.linno = 1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014468
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014469 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
Denys Vlasenko458c1f22016-10-27 23:51:19 +020014470 setsignal(SIGCHLD);
14471
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014472 {
14473 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014474 const char *p;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014475
14476 initvar();
14477 for (envp = environ; envp && *envp; envp++) {
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010014478/* Used to have
14479 * p = endofname(*envp);
14480 * if (p != *envp && *p == '=') {
14481 * here to weed out badly-named variables, but this breaks
14482 * scenarios where people do want them passed to children:
14483 * import os
14484 * os.environ["test-test"]="test"
14485 * if os.fork() == 0:
14486 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
14487 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14488 */
14489 if (strchr(*envp, '=')) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014490 setvareq(*envp, VEXPORT|VTEXTFIXED);
14491 }
14492 }
14493
Denys Vlasenko67dae152018-08-05 13:59:35 +020014494 setvareq((char*)defifsvar, VTEXTFIXED);
Denys Vlasenkoe627ac92016-09-30 14:36:59 +020014495 setvareq((char*)defoptindvar, VTEXTFIXED);
14496
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014497 setvar0("PPID", utoa(getppid()));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014498#if BASH_SHLVL_VAR
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014499 p = lookupvar("SHLVL");
Denys Vlasenko5680e982014-01-07 16:12:48 +010014500 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014501#endif
14502#if BASH_HOSTNAME_VAR
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014503 if (!lookupvar("HOSTNAME")) {
14504 struct utsname uts;
14505 uname(&uts);
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014506 setvar0("HOSTNAME", uts.nodename);
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014507 }
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014508#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014509 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014510 if (p) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014511 struct stat st1, st2;
Denys Vlasenkoef159702016-09-01 11:16:22 +020014512 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014513 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
14514 ) {
Denys Vlasenkoef159702016-09-01 11:16:22 +020014515 p = NULL;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014516 }
14517 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014518 setpwd(p, 0);
14519 }
14520}
14521
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014522
14523//usage:#define ash_trivial_usage
Denys Vlasenkoaaf3d5b2021-10-13 11:15:52 +020014524//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
Denys Vlasenko4e039ba2021-01-04 03:50:38 +010014525//////// comes from ^^^^^^^^^^optletters
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014526//usage:#define ash_full_usage "\n\n"
14527//usage: "Unix shell interpreter"
14528
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014529/*
14530 * Process the shell command line arguments.
14531 */
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014532static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000014533procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014534{
14535 int i;
14536 const char *xminusc;
14537 char **xargv;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014538 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014539
14540 xargv = argv;
Ron Yorston8767c122018-11-05 13:13:08 +000014541 login_sh = xargv[0] && xargv[0][0] == '-';
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014542#if NUM_SCRIPTS > 0
14543 if (minusc)
14544 goto setarg0;
14545#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014546 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000014547 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014548 xargv++;
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014549 argptr = xargv;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014550 for (i = 0; i < NOPTS; i++)
14551 optlist[i] = 2;
Denys Vlasenko897475a2019-06-01 16:35:09 +020014552 if (options(&login_sh)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000014553 /* it already printed err message */
14554 raise_exception(EXERROR);
14555 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014556 xargv = argptr;
14557 xminusc = minusc;
14558 if (*xargv == NULL) {
14559 if (xminusc)
14560 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
14561 sflag = 1;
14562 }
Denys Vlasenkof3634582019-06-03 12:21:04 +020014563 if (iflag == 2 /* no explicit -i given */
14564 && sflag == 1 /* -s given (or implied) */
14565 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
14566 && isatty(0) && isatty(1) /* we are on tty */
14567 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014568 iflag = 1;
Denys Vlasenkof3634582019-06-03 12:21:04 +020014569 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014570 if (mflag == 2)
14571 mflag = iflag;
Denys Vlasenko85158b62021-01-03 12:14:58 +010014572 /* Unset options which weren't explicitly set or unset */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014573 for (i = 0; i < NOPTS; i++)
Denys Vlasenko85158b62021-01-03 12:14:58 +010014574 optlist[i] &= 1; /* same effect as "if (optlist[i] == 2) optlist[i] = 0;" */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014575#if DEBUG == 2
14576 debug = 1;
14577#endif
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014578 /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014579 if (xminusc) {
14580 minusc = *xargv++;
14581 if (*xargv)
14582 goto setarg0;
14583 } else if (!sflag) {
14584 setinputfile(*xargv, 0);
14585 setarg0:
14586 arg0 = *xargv++;
14587 commandname = arg0;
14588 }
14589
14590 shellparam.p = xargv;
14591#if ENABLE_ASH_GETOPTS
14592 shellparam.optind = 1;
14593 shellparam.optoff = -1;
14594#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014595 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014596 while (*xargv) {
14597 shellparam.nparam++;
14598 xargv++;
14599 }
Denys Vlasenko31df5a32020-12-13 16:36:28 +010014600
14601 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
14602 * Try:
14603 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
14604 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
14605 * NB: must do it before setting up signals (in optschanged())
14606 * and reading .profile etc (after we return from here):
14607 */
14608 if (iflag)
14609 signal(SIGHUP, SIG_DFL);
14610
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014611 optschanged();
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014612
14613 return login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014614}
14615
14616/*
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014617 * Read /etc/profile, ~/.profile, $ENV.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014618 */
14619static void
14620read_profile(const char *name)
14621{
Denys Vlasenko46999802017-07-29 21:12:29 +020014622 name = expandstr(name, DQSYNTAX);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014623 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
14624 return;
Denys Vlasenko0840c912016-10-01 15:27:44 +020014625 cmdloop(0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014626 popfile();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014627}
14628
14629#if PROFILE
14630static short profile_buf[16384];
14631extern int etext();
14632#endif
14633
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014634/*
14635 * Main routine. We initialize things, parse the arguments, execute
14636 * profiles if we're a login shell, and then call cmdloop to execute
14637 * commands. The setjmp call sets up the location to jump to when an
14638 * exception occurs. When an exception occurs the variable "state"
14639 * is used to figure out how far we had gotten.
14640 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000014641int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Ron Yorston8767c122018-11-05 13:13:08 +000014642#if NUM_SCRIPTS > 0
14643int ash_main(int argc, char **argv)
14644#else
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014645int ash_main(int argc UNUSED_PARAM, char **argv)
Ron Yorston8767c122018-11-05 13:13:08 +000014646#endif
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014647/* note: 'argc' is used only if embedded scripts are enabled */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014648{
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014649 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014650 struct jmploc jmploc;
14651 struct stackmark smark;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014652 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014653
Denis Vlasenko01631112007-12-16 17:20:38 +000014654 /* Initialize global data */
14655 INIT_G_misc();
14656 INIT_G_memstack();
14657 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014658#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000014659 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014660#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014661 INIT_G_cmdtable();
14662
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014663#if PROFILE
14664 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
14665#endif
14666
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014667 state = 0;
14668 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014669 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014670 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014671
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014672 exitreset();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014673
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014674 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014675 s = state;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014676 if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014677 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020014678 }
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014679
14680 reset();
14681
Denys Vlasenkob563f622010-09-25 17:15:13 +020014682 if (e == EXINT) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020014683 newline_and_flush(stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020014684 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014685
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014686 popstackmark(&smark);
14687 FORCE_INT_ON; /* enable interrupts */
14688 if (s == 1)
14689 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014690 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014691 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014692 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014693 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014694 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014695 }
14696 exception_handler = &jmploc;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014697 rootpid = getpid();
14698
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014699 init();
14700 setstackmark(&smark);
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014701
14702#if NUM_SCRIPTS > 0
14703 if (argc < 0)
14704 /* Non-NULL minusc tells procargs that an embedded script is being run */
14705 minusc = get_script_content(-argc - 1);
14706#endif
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014707 login_sh = procargs(argv);
Denys Vlasenko474ed062016-10-30 18:30:29 +010014708#if DEBUG
14709 TRACE(("Shell args: "));
14710 trace_puts_args(argv);
14711#endif
Denis Vlasenko68404f12008-03-17 09:00:54 +000014712
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014713 if (login_sh) {
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014714 const char *hp;
14715
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014716 state = 1;
14717 read_profile("/etc/profile");
14718 state1:
14719 state = 2;
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014720 hp = lookupvar("HOME");
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014721 if (hp)
14722 read_profile("$HOME/.profile");
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014723 }
14724 state2:
14725 state = 3;
Denys Vlasenko62f1eed2021-10-12 22:39:11 +020014726 if (iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014727#ifndef linux
Denys Vlasenko62f1eed2021-10-12 22:39:11 +020014728 && getuid() == geteuid() && getgid() == getegid()
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014729#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014730 ) {
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014731 const char *shinit = lookupvar("ENV");
14732 if (shinit != NULL && *shinit != '\0')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014733 read_profile(shinit);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014734 }
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014735 popstackmark(&smark);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014736 state3:
14737 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014738 if (minusc) {
14739 /* evalstring pushes parsefile stack.
14740 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000014741 * is one of stacked source fds.
14742 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenkof3634582019-06-03 12:21:04 +020014743
Denys Vlasenko79b3d422010-06-03 04:29:08 +020014744 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020014745 // ^^ not necessary since now we special-case fd 0
Denys Vlasenko035486c2017-07-31 04:09:19 +020014746 // in save_fd_on_redirect()
Denys Vlasenkof3634582019-06-03 12:21:04 +020014747
Denys Vlasenko64aa86b2021-09-07 18:16:45 +020014748 lineno = 0; // bash compat
Denys Vlasenkof3634582019-06-03 12:21:04 +020014749 // dash: evalstring(minusc, sflag ? 0 : EV_EXIT);
14750 // The above makes
14751 // ash -sc 'echo $-'
14752 // continue reading input from stdin after running 'echo'.
14753 // bash does not do this: it prints "hBcs" and exits.
14754 evalstring(minusc, EV_EXIT);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014755 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014756
14757 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020014758#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014759 if (line_input_state) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014760 const char *hp = lookupvar("HISTFILE");
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014761 if (!hp) {
14762 hp = lookupvar("HOME");
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014763 if (hp) {
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014764 INT_OFF;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014765 hp = concat_path_file(hp, ".ash_history");
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014766 setvar0("HISTFILE", hp);
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014767 free((char*)hp);
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014768 INT_ON;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014769 hp = lookupvar("HISTFILE");
14770 }
14771 }
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014772 if (hp)
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014773 line_input_state->hist_file = xstrdup(hp);
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020014774# if ENABLE_FEATURE_SH_HISTFILESIZE
14775 hp = lookupvar("HISTFILESIZE");
14776 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14777# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014778 }
14779#endif
14780 state4: /* XXX ??? - why isn't this before the "if" statement */
14781 cmdloop(1);
14782 }
14783#if PROFILE
14784 monitor(0);
14785#endif
14786#ifdef GPROF
14787 {
14788 extern void _mcleanup(void);
14789 _mcleanup();
14790 }
14791#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020014792 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014793 exitshell();
14794 /* NOTREACHED */
14795}
14796
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014797
Eric Andersendf82f612001-06-28 07:46:40 +000014798/*-
14799 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000014800 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000014801 *
14802 * This code is derived from software contributed to Berkeley by
14803 * Kenneth Almquist.
14804 *
14805 * Redistribution and use in source and binary forms, with or without
14806 * modification, are permitted provided that the following conditions
14807 * are met:
14808 * 1. Redistributions of source code must retain the above copyright
14809 * notice, this list of conditions and the following disclaimer.
14810 * 2. Redistributions in binary form must reproduce the above copyright
14811 * notice, this list of conditions and the following disclaimer in the
14812 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000014813 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000014814 * may be used to endorse or promote products derived from this software
14815 * without specific prior written permission.
14816 *
Denys Vlasenko95f79532017-08-02 14:26:33 +020014817 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
Eric Andersendf82f612001-06-28 07:46:40 +000014818 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14819 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14820 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
14821 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14822 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14823 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14824 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
14825 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
14826 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
14827 * SUCH DAMAGE.
14828 */